diff --git a/lib/core.ts b/lib/core.ts index 2dc232a..0199814 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -37,6 +37,7 @@ interface Internals { var env: Environment = arguments[0], internals: Internals = arguments[1]; globalThis.log = internals.constructor.log; +var i = 0.0; try { run('values/object'); diff --git a/lib/values/errors.ts b/lib/values/errors.ts index 4bfc7e5..a8d1ef0 100644 --- a/lib/values/errors.ts +++ b/lib/values/errors.ts @@ -2,11 +2,12 @@ define("values/errors", () => { var Error = env.global.Error = function Error(msg: string) { if (msg === undefined) msg = ''; else msg += ''; - - return Object.setPrototypeOf({ + + return { message: msg, stack: [] as string[], - }, Error.prototype); + __proto__: Error.prototype, + } as any; } as ErrorConstructor; setConstr(Error.prototype, Error); diff --git a/src/me/topchetoeu/jscript/exceptions/EngineException.java b/src/me/topchetoeu/jscript/exceptions/EngineException.java index 75adb6a..6119a73 100644 --- a/src/me/topchetoeu/jscript/exceptions/EngineException.java +++ b/src/me/topchetoeu/jscript/exceptions/EngineException.java @@ -44,8 +44,9 @@ public class EngineException extends RuntimeException { 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); + if (name != null) res.defineProperty(null, "name", name); res.defineProperty(null, "message", msg); return res; } @@ -57,19 +58,22 @@ public class EngineException extends RuntimeException { 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) { - return new EngineException(err(msg, PlaceholderProto.ERROR)); + return new EngineException(err(null, msg, PlaceholderProto.ERROR)); } 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) { - return new EngineException(err(msg, PlaceholderProto.SYNTAX_ERROR)); + return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR)); } 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) { - return new EngineException(err(msg, PlaceholderProto.RANGE_ERROR)); + return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR)); } } diff --git a/src/me/topchetoeu/jscript/filesystem/EntryType.java b/src/me/topchetoeu/jscript/filesystem/EntryType.java new file mode 100644 index 0000000..26e255a --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/EntryType.java @@ -0,0 +1,7 @@ +package me.topchetoeu.jscript.filesystem; + +public enum EntryType { + NONE, + FILE, + FOLDER, +} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/File.java b/src/me/topchetoeu/jscript/filesystem/File.java new file mode 100644 index 0000000..b7a14a8 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/File.java @@ -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); + } +} \ No newline at end of file diff --git a/src/me/topchetoeu/jscript/filesystem/Filesystem.java b/src/me/topchetoeu/jscript/filesystem/Filesystem.java new file mode 100644 index 0000000..02ae9d5 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/Filesystem.java @@ -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; +} diff --git a/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java b/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java new file mode 100644 index 0000000..2cc59b4 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/InaccessibleFile.java @@ -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() { } +} diff --git a/src/me/topchetoeu/jscript/filesystem/MemoryFile.java b/src/me/topchetoeu/jscript/filesystem/MemoryFile.java new file mode 100644 index 0000000..c414a7e --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/MemoryFile.java @@ -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; + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/Permissions.java b/src/me/topchetoeu/jscript/filesystem/Permissions.java new file mode 100644 index 0000000..5a5ff34 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/Permissions.java @@ -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; + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java new file mode 100644 index 0000000..cb5d067 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/PhysicalFile.java @@ -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; + } +} diff --git a/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java b/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java new file mode 100644 index 0000000..0241676 --- /dev/null +++ b/src/me/topchetoeu/jscript/filesystem/PhysicalFilesystem.java @@ -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; + } +} diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index cf95238..0672cb5 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -516,12 +516,59 @@ public class Parsing { for (int i = 2; i < literal.length(); i++) { res *= 16; - res += fromHex(literal.charAt(i)); + int dig = fromHex(literal.charAt(i)); + res += dig; } return res; } + 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 parseTokens(String filename, Collection tokens) { var res = new ArrayList(); @@ -529,28 +576,14 @@ public class Parsing { var loc = new Location(el.line, el.start, filename); switch (el.type) { case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break; - case NUMBER: - if (el.value.startsWith("0x") || el.value.startsWith("0X")) { - if (el.value.endsWith("x") || el.value.endsWith("X")) { - throw new SyntaxException(loc, "Invalid number format."); - } - res.add(Token.number(el.line, el.start, parseHex(el.value))); break; - } - if ( - el.value.endsWith("e") || el.value.endsWith("E") || el.value.endsWith("-") - ) throw new SyntaxException(loc, "Invalid number format."); - else res.add(Token.number(el.line, el.start, Double.parseDouble(el.value))); break; + case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value))); 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; case OPERATOR: Operator op = Operator.parse(el.value); if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value)); res.add(Token.operator(el.line, el.start, op)); break; - case STRING: - res.add(Token.string(el.line, el.start, parseString(loc, el.value))); - break; - case REGEX: - res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value))); - break; } }