Compare commits

..

No commits in common. "master" and "v0.10.4-beta" have entirely different histories.

206 changed files with 3568 additions and 7972 deletions

View File

@ -25,14 +25,10 @@ jobs:
gradle-version: "8.10"
- name: Build
run: gradle build
- name: Publish
run: gradle publish
env:
ACCESS_TOKEN: "${{secrets.PACKAGE_TOKEN}}"
REPO_URL: "${{github.server_url}}/api/packages/${{github.repository_owner}}/maven"
- name: Create release
uses: "https://gitea.com/actions/gitea-release-action@main"
with:
# api_key: "${{secrets.TOKEN}}"
files: |
LICENSE
build/libs/*.jar

View File

@ -2,76 +2,22 @@
**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. A small REPL (`me.topchetoeu.j2s.repl.SimpleRepl`) library with an included simple debugger (`me.topchetoeu.j2s.repl.debug.SimpleDebugger`). These are more or less reference implementations. In the future, most of the primordials logic of `SimpleRepl` will be moved in the "lib" project, but for now, it will stay there.
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.
## How to use?
## Example
Since this is mostly targeted for integration into other applications, here, examples for invoking JS code from Java will be shown. In the future, a more comprehensive wiki will be made.
### Setting up an event loop
First of all, you will want to create an event loop. While not required, 99% of the times you will want to have one.
The following is going to execute a simple javascript statement:
```java
var engine = new Engine();
var thread = engine.start();
// 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
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);
// Get our result
System.out.println(awaitable.await());
```
Hooray! Now you have an event loop. The thread that was automatically created is a daemon thread, so it will harakiri when the rest of the application ends. If you don't want to use the built-in thread, you can instead run it with `engine.run(untilEmpty)`. If you pass true (which you most likely need), the event loop will be run blocking-ly until it is empty. Otherwise, it will be run forever.
### Creating the execution environment
This is one of the other crucial parts of J2S's architecture - the environment. It contains the global scope, a reference to the event loop, the global scope, the debugger, source mappings and a lot more. To run JS code, you must create an environment:
```java
var env = Environment.empty();
env.add(EventLoop.KEY, engine); // Gives
env.add(DebugContext.KEY, new DebugContext()); // For source mappings
```
As you can see, the environment is nothing more than a simple map of objects that may be of interest to the JS code. Although you can do much more with the environment, we will leave it at that.
### Registering the compiler
Since the compiler is a part of the runtime, you need to register it in the environment. You can use the following boilerplate, although a nicer API will be exposed later on:
```java
env.add(Compiler.KEY, (_env, filename, raw, mapper) -> {
try {
// Invokes the compiler. Will return a CompilerResult, which, along other things,
// gives us all the compiled function bodies (aka function scaffoldings, that can be used to construct a function value)
var res = JavaScript.compile(env, filename, raw, true);
var body = res.body();
// We'll register the source and function source mappings for debugging
DebugContext.get(env).onSource(filename, raw);
for (var el : res.all()) {
DebugContext.get(env).onFunctionLoad(el.body(), el.map(mapper));
}
// Finally, we will construct the function
// Few things to note: we need to pass the environment, the name of the function (the filename),
// and the last thing: the captures. Since we are compiling the main function, we don't have
// any captures, so we pass an empty array
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
}
catch (SyntaxException e) {
// Convert the exception to an engine exception
var res = EngineException.ofSyntax(e.msg);
// Add the location of the error to its stack trace
res.add(env, e.loc.filename() + "", e.loc);
throw res;
}
});
```
### Evaluating a piece of code on the event loop
This is what you really want to do: run code! You can do that in the following way:
```java
var result = engine.pushMsg(false, env, Filename.parse("my-app://test.js"), "return 10 + 5 / 3;", Value.UNDEFINED).get();
System.out.println(result.toReadable(env));
```
If all goes well, we will get "11.666..." as a result.

View File

@ -11,7 +11,7 @@ tasks.wrapper {
}
tasks.build {
for (proj in subprojects) {
subprojects.forEach { proj ->
dependsOn(proj.tasks.named("build"));
doLast {
copy {

View File

@ -9,7 +9,4 @@ java {
toolchain {
languageVersion = JavaLanguageVersion.of(17);
}
withJavadocJar();
withSourcesJar();
}

View File

@ -1,6 +1,5 @@
plugins {
id("java");
id("maven-publish");
}
version = rootProject.version;
@ -21,27 +20,3 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2");
testRuntimeOnly("org.junit.platform:junit-platform-launcher");
}
publishing {
repositories {
maven {
name = "Gitea";
url = uri(System.getenv("REPO_URL") ?: "");
credentials(HttpHeaderCredentials::class) {
name = "Authorization";
value = "token ${System.getenv("ACCESS_TOKEN")}";
}
authentication {
create<HttpHeaderAuthentication>("header");
}
}
}
publications {
create<MavenPublication>("maven") {
from(components["java"]);
}
}
}

View File

@ -5,7 +5,7 @@ plugins {
description = "A collection of utils and structures for the rest of the project";
tasks.processResources {
filesMatching("metadata", {
filesMatching("metadata.json", {
expand(
"version" to properties["project_version"],
"name" to properties["project_name"],

View File

@ -1,62 +0,0 @@
package me.topchetoeu.j2s.common;
import java.util.Arrays;
import java.util.regex.Pattern;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
public interface FunctionMap {
public static final FunctionMap EMPTY = new FunctionMap() {
@Override public Location first() {
return null;
}
@Override public Location last() {
return null;
}
@Override public Location toLocation(int i, boolean approximate) {
return null;
}
@Override public BreakpointType getBreakpoint(int i) {
return BreakpointType.NONE;
}
@Override public Iterable<Location> breakpoints(Location start, Location end) {
return Arrays.asList();
}
@Override public Location correctBreakpoint(Location i) {
return null;
}
@Override public Iterable<Location> correctBreakpoint(Pattern filename, int line, int column) {
return Arrays.asList();
}
@Override public String[] localNames() {
return null;
}
@Override public String[] capturableNames() {
return null;
}
@Override public String[] captureNames() {
return null;
}
};
Location first();
Location last();
Location toLocation(int i, boolean approximate);
default Location toLocation(int i) {
return toLocation(i, false);
}
BreakpointType getBreakpoint(int i);
Location correctBreakpoint(Location i);
Iterable<Location> correctBreakpoint(Pattern filename, int line, int column);
Iterable<Location> breakpoints(Location start, Location end);
String[] localNames();
String[] capturableNames();
String[] captureNames();
}

View File

@ -4,6 +4,8 @@ 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),
@ -31,6 +33,7 @@ public class Instruction {
LOAD_FUNC(0x30),
LOAD_ARR(0x31),
LOAD_OBJ(0x32),
LOAD_REGEX(0x33),
LOAD_GLOB(0x38),
LOAD_INTRINSICS(0x39),
@ -51,11 +54,15 @@ public class Instruction {
STORE_MEMBER_INT(0x4A),
STORE_MEMBER_STR(0x4B),
GLOB_GET(0x50),
GLOB_SET(0x51),
GLOB_DEF(0x52),
DEF_PROP(0x50),
DEF_FIELD(0x51),
KEYS(0x52),
TYPEOF(0x53),
OPERATION(0x54),
OPERATION(0x56);
GLOB_GET(0x60),
GLOB_SET(0x61),
GLOB_DEF(0x62);
private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric;
@ -282,6 +289,9 @@ public class Instruction {
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];
@ -303,8 +313,8 @@ public class Instruction {
return new Instruction(Type.DUP, count, offset);
}
public static Instruction storeVar(int i, boolean keep) {
return new Instruction(Type.STORE_VAR, i, keep);
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
return new Instruction(Type.STORE_VAR, i, keep, initialize);
}
public static Instruction storeMember() {
@ -332,6 +342,24 @@ public class Instruction {
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);
}

View File

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

View File

@ -1,58 +1,30 @@
package me.topchetoeu.j2s.common;
import me.topchetoeu.j2s.common.json.JSON;
import me.topchetoeu.j2s.common.parsing.Filename;
public class Metadata {
private static String VERSION;
private static String AUTHOR;
private static String NAME;
private static final String VERSION;
private static final String AUTHOR;
private static final String NAME;
static {
var raw = Reading.resourceToString("metadata").split("\n");
var line = 0;
var file = "internal://metadata";
for (var el : raw) {
line++;
el = el.trim();
if (el.startsWith("#")) continue;
if (el.isEmpty()) continue;
var i = el.indexOf(":");
if (i < 0) throw new RuntimeException(String.format("%s:%s: Expected colon on line", file, line));
var name = el.substring(0, i).trim();
var value = el.substring(i + 1).trim();
switch (name) {
case "version":
VERSION = value;
break;
case "author":
AUTHOR = value;
break;
case "name":
NAME = value;
break;
default:
throw new RuntimeException(String.format("%s:%s: Unexpected metadata key '%s'", file, line, name));
}
}
if (VERSION == null) throw new RuntimeException(String.format("%s:%s: No version specified", file, line));
if (AUTHOR == null) throw new RuntimeException(String.format("%s:%s: No author specified", file, line));
if (NAME == null) throw new RuntimeException(String.format("%s:%s: No name specified", file, line));
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";
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
else return VERSION;
}
public static String author() {
if (AUTHOR.equals("${AUTHOR}")) return "anonymous";
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
else return AUTHOR;
}
public static String name() {
if (NAME.equals("${NAME}")) return "some-product";
if (NAME.equals("$" + "{NAME}")) return "some-product";
else return NAME;
}
}

View File

@ -3,37 +3,36 @@ package me.topchetoeu.j2s.common;
import java.util.HashMap;
public enum Operation {
TYPEOF(0x10, 1),
INSTANCEOF(0x11, 2),
IN(0x12, 2),
INSTANCEOF(1, 2),
IN(2, 2),
MULTIPLY(0x20, 2),
DIVIDE(0x21, 2),
MODULO(0x22, 2),
ADD(0x23, 2),
SUBTRACT(0x24, 2),
MULTIPLY(3, 2),
DIVIDE(4, 2),
MODULO(5, 2),
ADD(6, 2),
SUBTRACT(7, 2),
USHIFT_RIGHT(0x30, 2),
SHIFT_RIGHT(0x31, 2),
SHIFT_LEFT(0x32, 2),
USHIFT_RIGHT(8, 2),
SHIFT_RIGHT(9, 2),
SHIFT_LEFT(10, 2),
GREATER(0x40, 2),
LESS(0x41, 2),
GREATER_EQUALS(0x42, 2),
LESS_EQUALS(0x43, 2),
LOOSE_EQUALS(0x44, 2),
LOOSE_NOT_EQUALS(0x45, 2),
EQUALS(0x46, 2),
NOT_EQUALS(0x47, 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(0x50, 2),
OR(0x51, 2),
XOR(0x52, 2),
AND(19, 2),
OR(20, 2),
XOR(21, 2),
NEG(0x60, 1),
POS(0x61, 1),
NOT(0x62, 1),
INVERSE(0x63, 1);
NEG(23, 1),
POS(24, 1),
NOT(25, 1),
INVERSE(26, 1);
private static final HashMap<Integer, Operation> operations = new HashMap<>();

View File

@ -1,123 +0,0 @@
package me.topchetoeu.j2s.common;
import java.math.BigDecimal;
public class StringifyUtils {
public static String quoteString(String raw) {
var res = new StringBuilder("\"");
var alphabet = "0123456789ABCDEF".toCharArray();
for (var c : raw.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();
}
public static String quoteNumber(Double num) {
if (num == Double.NEGATIVE_INFINITY) return "-Infinity";
if (num == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(num)) return "NaN";
return BigDecimal.valueOf(num).stripTrailingZeros().toPlainString();
}
private static double power(double a, long b) {
if (b == 0) return 1;
if (b == 1) return a;
if (b < 0) return 1 / power(a, -b);
if ((b & 1) == 0) return power(a * a, b / 2);
else return a * power(a * a, b / 2);
}
public static Double unqoteNumber(String src) {
var i = 0;
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (src.charAt(i) == '-') {
negative = true;
i++;
}
while (i < src.length()) {
var c = src.charAt(i);
if (c < '0' || c > '9') break;
parsedAny = true;
whole *= 10;
whole += src.charAt(i++) - '0';
}
if (i < src.length() && src.charAt(i) == '.') {
parsedAny = true;
i++;
while (i < src.length()) {
var c = src.charAt(i);
if (c < '0' || c > '9') break;
parsedAny = true;
fract += src.charAt(i++) - '0';
fract /= 10;
}
}
if (i < src.length() && (src.charAt(i) == 'e' || src.charAt(i) == 'E')) {
i++;
parsedAny = true;
boolean expNegative = false;
boolean parsedE = false;
if (i < src.length()) {
if (src.charAt(i) == '-') {
expNegative = true;
i++;
}
else if (src.charAt(i) == '+') {
i++;
}
}
while (i < src.length()) {
var c = src.charAt(i);
if (c < '0' || c > '9') break;
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.charAt(i) - '0';
else exponent += src.charAt(i) - '0';
}
if (!parsedE) return Double.NaN;
}
if (i != src.length()) return Double.NaN;
if (!parsedAny) {
if (negative) return Double.NaN;
return 0.;
}
else if (negative) return -(whole + fract) * power(10, exponent);
else return (whole + fract) * power(10, exponent);
}
}

View File

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

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.common;
package me.topchetoeu.j2s.common.environment;
import java.util.HashMap;
import java.util.HashSet;
@ -63,12 +63,9 @@ public class Environment {
}
public <T> T init(Key<T> key, T val) {
if (!has(key)) {
this.add(key, val);
if (!has(key)) this.add(key, val);
return val;
}
else return get(key);
}
public <T> T initFrom(Key<T> key, Supplier<T> val) {
if (!has(key)) {
var res = val.get();

View File

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

View File

@ -1,16 +1,16 @@
package me.topchetoeu.j2s.compilation.json;
package me.topchetoeu.j2s.common.json;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.StringifyUtils;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
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) {
@ -110,10 +110,37 @@ public class JSON {
}
public static String stringify(JSONElement el) {
if (el.isNumber()) return StringifyUtils.quoteNumber(el.number());
if (el.isString()) return StringifyUtils.quoteString(el.string());
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++) {
@ -129,7 +156,7 @@ public class JSON {
for (int i = 0; i < entries.size(); i++) {
if (i != 0) res.append(",");
res.append(StringifyUtils.quoteString(entries.get(i).getKey()));
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
res.append(":");
res.append(stringify(entries.get(i).getValue()));
}

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.compilation.json;
package me.topchetoeu.j2s.common.json;
public class JSONElement {
public static enum Type {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.compilation.json;
package me.topchetoeu.j2s.common.json;
import java.util.ArrayList;
import java.util.Arrays;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.compilation.json;
package me.topchetoeu.j2s.common.json;
import java.util.Collection;
import java.util.HashMap;

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.compilation;
package me.topchetoeu.j2s.common.mapping;
import java.util.ArrayList;
import java.util.Arrays;
@ -13,14 +13,12 @@ import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.FunctionMap;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.Location;
public final class CompilationFunctionMap implements FunctionMap {
public class FunctionMap {
public static class FunctionMapBuilder {
private Location first, last;
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
@ -35,9 +33,6 @@ public final class CompilationFunctionMap implements FunctionMap {
}
public FunctionMapBuilder setLocation(int i, Location loc) {
if (loc == null || i < 0) return this;
if (first == null || first.compareTo(loc) > 0) first = loc;
if (last == null || last.compareTo(loc) < 0) last = loc;
sourceMap.put(i, loc);
return this;
}
@ -48,10 +43,12 @@ public final class CompilationFunctionMap implements FunctionMap {
}
public Location first() {
return first;
if (sourceMap.size() == 0) return null;
return sourceMap.firstEntry().getValue();
}
public Location last() {
return last;
if (sourceMap.size() == 0) return null;
return sourceMap.lastEntry().getValue();
}
public FunctionMapBuilder map(Function<Location, Location> mapper) {
@ -78,17 +75,17 @@ public final class CompilationFunctionMap implements FunctionMap {
return this;
}
public CompilationFunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
return new CompilationFunctionMap(sourceMap, breakpoints, first, last, localNames, capturableNames, captureNames);
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
return new FunctionMap(sourceMap, breakpoints, localNames, capturableNames, captureNames);
}
public CompilationFunctionMap build() {
return new CompilationFunctionMap(sourceMap, breakpoints, first, last, new String[0], new String[0], new String[0]);
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 CompilationFunctionMap EMPTY = new CompilationFunctionMap();
public static final FunctionMap EMPTY = new FunctionMap();
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
@ -96,29 +93,28 @@ public final class CompilationFunctionMap implements FunctionMap {
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
public final String[] localNames, capturableNames, captureNames;
public final Location first, last;
@Override public Location toLocation(int pc, boolean approximate) {
public Location toLocation(int pc, boolean approxiamte) {
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
var res = pcToLoc.get(pc);
if (!approximate || res != null) return res;
if (!approxiamte || res != null) return res;
var entry = pcToLoc.headMap(pc, true).lastEntry();
if (entry == null) return null;
else return entry.getValue();
}
@Override public Location toLocation(int pc) {
public Location toLocation(int pc) {
return toLocation(pc, false);
}
@Override public BreakpointType getBreakpoint(int pc) {
public BreakpointType getBreakpoint(int pc) {
return bps.getOrDefault(pc, BreakpointType.NONE);
}
@Override public Location correctBreakpoint(Location loc) {
public Location correctBreakpoint(Location loc) {
var set = bpLocs.get(loc.filename());
if (set == null) return null;
else return set.ceiling(loc);
}
@Override public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
var candidates = new HashMap<Filename, TreeSet<Location>>();
for (var name : bpLocs.keySet()) {
@ -136,7 +132,7 @@ public final class CompilationFunctionMap implements FunctionMap {
return res;
}
@Override public List<Location> breakpoints(Location start, Location end) {
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();
@ -156,25 +152,8 @@ public final class CompilationFunctionMap implements FunctionMap {
return pcToLoc.lastEntry().getValue();
}
@Override public Location first() {
return first;
}
@Override public Location last() {
return last;
}
@Override public String[] capturableNames() {
return capturableNames;
}
@Override public String[] captureNames() {
return captureNames;
}
@Override public String[] localNames() {
return localNames;
}
public CompilationFunctionMap clone() {
var res = new CompilationFunctionMap(new HashMap<>(), new HashMap<>(), first, last, localNames, capturableNames, captureNames);
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);
@ -182,7 +161,7 @@ public final class CompilationFunctionMap implements FunctionMap {
return res;
}
public CompilationFunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, Location first, Location last, String[] localNames, String[] capturableNames, String[] captureNames) {
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()) {
@ -201,16 +180,11 @@ public final class CompilationFunctionMap implements FunctionMap {
this.localNames = localNames;
this.captureNames = captureNames;
this.capturableNames = capturableNames;
this.first = first;
this.last = last;
}
private CompilationFunctionMap() {
private FunctionMap() {
localNames = new String[0];
captureNames = new String[0];
capturableNames = new String[0];
first = null;
last = null;
}
public static FunctionMapBuilder builder() {

View File

@ -1,4 +1,4 @@
package me.topchetoeu.j2s.common;
package me.topchetoeu.j2s.common.parsing;
import java.io.File;

View File

@ -1,8 +1,10 @@
package me.topchetoeu.j2s.common;
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);
@ -30,9 +32,9 @@ public abstract class Location implements Comparable<Location> {
};
}
public final Location nextLine() {
return nextLine(1);
return changeLine(1);
}
public final Location nextLine(int offset) {
public final Location changeLine(int offset) {
var self = this;
return new Location() {
@ -58,14 +60,7 @@ public abstract class Location implements Comparable<Location> {
}
@Override public int compareTo(Location other) {
int a;
var filenameA = filename();
var filenameB = other.filename();
if (filenameB == null && filenameA == null) a = 0;
else if (filenameA == null) a = -1;
else if (filenameB == null) a = 1;
else a = filenameA.toString().compareTo(filenameB.toString());
int a = filename().toString().compareTo(other.filename().toString());
int b = Integer.compare(line(), other.line());
int c = Integer.compare(start(), other.start());
@ -84,27 +79,21 @@ public abstract class Location implements Comparable<Location> {
}
public static Location of(String raw) {
var i1 = raw.lastIndexOf(':');
if (i1 < 0) return Location.of(Filename.parse(raw), -1, -1);
var i0 = raw.lastIndexOf(':');
if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1);
var i0 = raw.substring(0, i1).lastIndexOf(':', i1);
var i1 = raw.lastIndexOf(':', i0);
if (i0 < 0) {
try {
return Location.of(null, Integer.parseInt(raw.substring(0, i1)) - 1, Integer.parseInt(raw.substring(i1 + 1)) - 1);
return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1);
}
catch (NumberFormatException e) {}
try {
return Location.of(Filename.parse(raw.substring(0, i1)), Integer.parseInt(raw.substring(i1 + 1)) - 1, -1);
}
catch (NumberFormatException e) {}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
}
int start, line;
try {
start = Integer.parseInt(raw.substring(i1 + 1));
}
@ -116,9 +105,9 @@ public abstract class Location implements Comparable<Location> {
line = Integer.parseInt(raw.substring(i0 + 1, i1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw.substring(0, i1)), start - 1, -1);
return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1);
}
return Location.of(Filename.parse(raw.substring(0, i0)), line - 1, start - 1);
return Location.of(Filename.parse(raw.substring(0, i0)), start, line);
}
}

View File

@ -1,6 +1,4 @@
package me.topchetoeu.j2s.compilation.parsing;
import me.topchetoeu.j2s.common.Location;
package me.topchetoeu.j2s.common.parsing;
public class ParseRes<T> {
public static enum State {
@ -35,20 +33,19 @@ public class ParseRes<T> {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, this.n + n);
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
return (ParseRes<T2>)this;
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;
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;
return (ParseRes<T2>) this;
}
public boolean isSuccess() { return state.isSuccess(); }
@ -66,6 +63,7 @@ public class ParseRes<T> {
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);

View File

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

View File

@ -1,4 +1,6 @@
package me.topchetoeu.j2s.compilation.parsing;
package me.topchetoeu.j2s.common.parsing;
import me.topchetoeu.j2s.common.SyntaxException;
public class Parsing {
public static boolean isDigit(Character c) {
@ -16,13 +18,14 @@ public class Parsing {
}
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')) {
if (n >= src.size()) return src.size();
n++;
}
while (!src.is(n, '\n')) n++;
n++;
}
@ -85,7 +88,7 @@ public class Parsing {
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) return ParseRes.error(src.loc(i + n), "Invalid hexadecimal escape sequence");
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence");
n++;
newC = (newC << 4) | val;
@ -100,7 +103,7 @@ public class Parsing {
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) return ParseRes.error(src.loc(i + n), "Invalid Unicode escape sequence");
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
n++;
newC = (newC << 4) | val;

View File

@ -1,10 +1,8 @@
package me.topchetoeu.j2s.compilation.parsing;
package me.topchetoeu.j2s.common.parsing;
import java.util.function.Predicate;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.environment.Environment;
public class Source {
public final Environment env;
@ -14,8 +12,6 @@ public class Source {
private int[] lineStarts;
public Location loc(int offset) {
if (offset < 0) offset = 0;
if (offset > src.length()) offset = src.length();
return new SourceLocation(filename, lineStarts, offset);
}
public boolean is(int i, char c) {

View File

@ -1,10 +1,7 @@
package me.topchetoeu.j2s.compilation.parsing;
package me.topchetoeu.j2s.common.parsing;
import java.util.Objects;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
public class SourceLocation extends Location {
private int[] lineStarts;
private int line;

View File

@ -1,3 +0,0 @@
version: ${version}
name: ${name}
author: TopchetoEU

View File

@ -0,0 +1,5 @@
{
"version": "${version}",
"name": "${name}",
"author": "TopchetoEU"
}

View File

@ -0,0 +1,14 @@
package me.topchetoeu.j2s;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestHelloWorld {
@Test
public void testHelloWorld() {
final String message = "Hello World!";
assertEquals("Hello World!", message);
}
}

View File

@ -1,69 +0,0 @@
package me.topchetoeu.j2s.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import org.junit.jupiter.api.Test;
public class TestEnvironment {
private final Key<String> FOO = new Key<>();
private final Key<Void> MARKER = new Key<>();
@Test public void testShouldCreate() {
new Environment();
Environment.empty();
}
@Test public void testShouldNotExist() {
var env = new Environment();
assertEquals(null, env.get(FOO));
assertEquals(false, env.has(FOO));
}
@Test public void testShouldAdd() {
var env = new Environment();
env.add(FOO, "test");
assertEquals("test", env.get(FOO));
}
@Test public void testShouldGetFromParent() {
var parent = new Environment();
parent.add(FOO, "test");
var child = parent.child();
assertEquals("test", child.get(FOO));
assertEquals(true, child.has(FOO));
}
@Test public void testShouldHideParent() {
var parent = new Environment();
parent.add(FOO, "test");
var child = parent.child();
child.remove(FOO);
assertEquals(null, child.get(FOO));
assertEquals(false, child.has(FOO));
}
@Test public void testShouldAddMarker() {
var env = new Environment();
env.add(MARKER);
assertEquals(true, env.has(MARKER));
assertEquals(false, env.hasNotNull(MARKER));
}
@Test public void testShouldInitOnce() {
var env = new Environment();
assertEquals("a", env.init(FOO, "a"));
assertEquals("a", env.init(FOO, "b"));
assertEquals("a", env.get(FOO));
}
@Test public void testShouldInitOnceFrom() {
var env = new Environment();
assertEquals("a", env.initFrom(FOO, () -> "a"));
assertEquals("a", env.initFrom(FOO, () -> "b"));
assertEquals("a", env.get(FOO));
}
@Test public void testShouldWrap() {
var env = new Environment();
assertEquals(env, Environment.wrap(env));
assertInstanceOf(Environment.class, Environment.wrap(null));
}
}

View File

@ -1,37 +0,0 @@
package me.topchetoeu.j2s.common;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestFilename {
@Test public void testShouldParseFilePath() {
var filename = Filename.parse("file://hello.world");
assertEquals("file", filename.protocol);
assertEquals("hello.world", filename.path);
}
@Test public void testShouldParseNoProtocolFilename() {
var filename = Filename.parse("hello.world");
assertEquals("file", filename.protocol);
assertEquals("hello.world", filename.path);
}
@Test public void testShouldParseAdditionalSlashFilename() {
var filename = Filename.parse("test:///hello.world");
assertEquals("test", filename.protocol);
assertEquals("/hello.world", filename.path);
}
@Test public void testShouldParseOneSlashFilename() {
var filename = Filename.parse("test:/hello.world");
assertEquals("file", filename.protocol);
assertEquals("test:/hello.world", filename.path);
}
@Test public void testShouldParseMatroshkaFilename() {
var a = Filename.parse("a://b://hello.world");
assertEquals("a", a.protocol);
assertEquals("b://hello.world", a.path);
var b = Filename.parse(a.path);
assertEquals("b", b.protocol);
assertEquals("hello.world", b.path);
}
}

View File

@ -1,159 +0,0 @@
package me.topchetoeu.j2s.common;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestLocation {
@Test public void testShouldCreateLocation() {
var loc = Location.of(new Filename("file", "test.txt"), 10, 5);
assertEquals("test.txt", loc.filename().path);
assertEquals(10, loc.line());
assertEquals(5, loc.start());
}
@Test public void testShouldGetNextLineLocation() {
var loc = Location.of("test.txt:10:5");
var next = loc.nextLine();
assertEquals(new Filename("file", "test.txt"), next.filename());
assertEquals(10, next.line());
assertEquals(0, next.start());
assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(9, loc.line());
assertEquals(4, loc.start());
}
@Test public void testShouldGetNextNthLineLocation() {
var loc = Location.of(new Filename("file", "test.txt"), 10, 5);
var next = loc.nextLine(5);
assertEquals(15, next.line());
assertEquals(0, next.start());
assertEquals(10, loc.line());
assertEquals(5, loc.start());
}
@Test public void testShouldGetNextLocation() {
var loc = Location.of("test:10:5");
var next = loc.add(10);
assertEquals(new Filename("file", "test"), next.filename());
assertEquals(9, next.line());
assertEquals(14, next.start());
assertEquals(new Filename("file", "test"), loc.filename());
assertEquals(9, loc.line());
assertEquals(4, loc.start());
}
@Test public void testShouldParseLocation() {
var loc = Location.of("test.txt:10:5");
assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(9, loc.line());
assertEquals(4, loc.start());
}
@Test public void testShouldParseComplexFilenameLocation() {
var loc = Location.of("testificate://test.txt:10:5");
assertEquals(new Filename("testificate", "test.txt"), loc.filename());
assertEquals(9, loc.line());
assertEquals(4, loc.start());
}
@Test public void testShouldParseNoFilenameLocation() {
var loc = Location.of("10:5");
assertEquals(null, loc.filename());
assertEquals(9, loc.line());
assertEquals(4, loc.start());
}
@Test public void testShouldParseNoStartLocationA() {
var loc = Location.of("file://10:5");
assertEquals(new Filename("file", "10"), loc.filename());
assertEquals(4, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testShouldParseNoStartLocationB() {
var loc = Location.of("file:5");
assertEquals(new Filename("file", "file"), loc.filename());
assertEquals(4, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testShouldParseOnlyFilenameLocationA() {
var loc = Location.of("http://example.org/test.txt");
assertEquals(new Filename("http", "example.org/test.txt"), loc.filename());
assertEquals(-1, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testShouldParseOnlyFilenameLocationB() {
var loc = Location.of("test.txt");
assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(-1, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testShouldParseOnlyFilenameWithColonLocation() {
var loc = Location.of("my-file:bad-file");
assertEquals(new Filename("file", "my-file:bad-file"), loc.filename());
assertEquals(-1, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testShouldParseOnlyFilenameWithTripleColonLocation() {
var loc = Location.of("a:my-file:bad-file");
assertEquals(new Filename("file", "a:my-file:bad-file"), loc.filename());
assertEquals(-1, loc.line());
assertEquals(-1, loc.start());
}
@Test public void testCompareEqualLoc() {
var locA = Location.of("test:10:5");
var locB = Location.of("test:10:5");
assertEquals(0, locA.compareTo(locB));
assertEquals(0, locB.compareTo(locA));
}
@Test public void testCompareNoFileLoc() {
var locA = Location.of("10:5");
var locB = Location.of("11:5");
assertEquals(-1, locA.compareTo(locB));
assertEquals(1, locB.compareTo(locA));
}
@Test public void testCompareOneNoFileLoc() {
var locA = Location.of("10:5");
var locB = Location.of("test:10:5");
assertEquals(-1, locA.compareTo(locB));
assertEquals(1, locB.compareTo(locA));
}
@Test public void testCompareDiffFileLoc() {
var locA = Location.of("a:10:5");
var locB = Location.of("b:10:5");
assertEquals(-1, locA.compareTo(locB));
assertEquals(1, locB.compareTo(locA));
}
@Test public void testCompareDiffLineLoc() {
var locA = Location.of("test:10:5");
var locB = Location.of("test:11:5");
assertEquals(-1, locA.compareTo(locB));
assertEquals(1, locB.compareTo(locA));
}
@Test public void testCompareDiffStartLoc() {
var locA = Location.of("test:10:5");
var locB = Location.of("test:10:10");
assertEquals(-1, locA.compareTo(locB));
assertEquals(1, locB.compareTo(locA));
}
@Test public void testToStringAll() {
var locA = Location.of("test:10:5");
assertEquals("file://test:10:5", locA.toString());
}
@Test public void testToStringNoFilename() {
var locA = Location.of("10:5");
assertEquals("10:5", locA.toString());
}
@Test public void testToStringNoStart() {
var locA = Location.of("file:5");
assertEquals("file://file:5", locA.toString());
}
@Test public void testToStringNoLoc() {
var locA = Location.of("file");
assertEquals("file://file", locA.toString());
}
}

View File

@ -4,6 +4,15 @@ plugins {
description = "A compiler of EcmaScript 5 code to J2S bytecode";
tasks.processResources {
filesMatching("metadata.json", {
expand(
"version" to properties["project_version"],
"name" to properties["project_name"],
);
});
}
tasks.test {
useJUnitPlatform();
}

View File

@ -2,29 +2,27 @@ package me.topchetoeu.j2s.compilation;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.function.Function;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.FunctionBody;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Key;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.compilation.CompilationFunctionMap.FunctionMapBuilder;
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.Iterator;
import java.util.LinkedList;
public final class CompileResult {
public static final Key<Void> DEBUG_LOG = new Key<>();
private FunctionBody body;
public final List<Instruction> instructions;
public final List<CompileResult> children;
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
@ -66,32 +64,10 @@ public final class CompileResult {
public void setLocation(Location type) {
setLocation(instructions.size() - 1, type);
}
public void setLocationAndDebug(Location loc, BreakpointType type) {
setLocationAndDebug(instructions.size() - 1, loc, type);
}
public Iterable<CompileResult> all() {
var stack = new Stack<CompileResult>();
stack.push(this);
return () -> new Iterator<CompileResult>() {
@Override public CompileResult next() {
if (stack.empty()) return null;
else {
var res = stack.pop();
for (var child : res.children) {
stack.push(child);
}
return res;
}
}
@Override public boolean hasNext() {
return !stack.empty();
}
};
}
public CompileResult addChild(FunctionNode node, CompileResult res) {
this.children.add(res);
this.childrenMap.put(node, res);
@ -103,15 +79,13 @@ public final class CompileResult {
return instructions.toArray(new Instruction[0]);
}
public CompilationFunctionMap map(Function<Location, Location> mapper) {
public FunctionMap map(Function<Location, Location> mapper) {
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public CompilationFunctionMap map() {
public FunctionMap map() {
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionBody body() {
if (body != null) return body;
var builtChildren = new FunctionBody[children.size()];
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
@ -126,7 +100,7 @@ public final class CompileResult {
for (var instr : instrRes) System.out.println(instr);
}
return body = new FunctionBody(
return new FunctionBody(
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren
);
@ -154,7 +128,7 @@ public final class CompileResult {
this.scope = scope;
this.instructions = new ArrayList<>();
this.children = new LinkedList<>();
this.map = CompilationFunctionMap.builder();
this.map = FunctionMap.builder();
this.env = env;
this.length = length;
}

View File

@ -5,11 +5,11 @@ import java.util.Arrays;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
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 {

View File

@ -2,13 +2,13 @@ package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
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;

View File

@ -3,8 +3,8 @@ package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;

View File

@ -3,8 +3,8 @@ package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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 {

View File

@ -6,10 +6,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Key;
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;
@ -23,9 +26,6 @@ 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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.values.ArgumentsNode;
import me.topchetoeu.j2s.compilation.values.ArrayNode;

View File

@ -5,11 +5,11 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.function.IntSupplier;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Key;
import me.topchetoeu.j2s.common.Location;
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<>();

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
public abstract class Node {
private Location loc;

View File

@ -1,6 +1,6 @@
package me.topchetoeu.j2s.compilation;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
public final class Parameter {
public final Location loc;

View File

@ -6,10 +6,10 @@ import java.util.List;
import com.github.bsideup.jabel.Desugar;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
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 {

View File

@ -1,15 +1,15 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class BreakNode extends Node {
public final String label;

View File

@ -1,15 +1,15 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class ContinueNode extends Node {
public final String label;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class DebugNode extends Node {
@Override public void compileFunctions(CompileResult target) {

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.VariableNode;
import me.topchetoeu.j2s.compilation.values.constants.BoolNode;
import me.topchetoeu.j2s.compilation.values.operations.IndexNode;

View File

@ -1,16 +1,16 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class DoWhileNode extends Node {
public final Node condition, body;

View File

@ -1,16 +1,16 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class ForInNode extends Node {
@ -31,18 +31,13 @@ public class ForInNode extends Node {
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadIntrinsics("keys"));
object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.pushValue(false));
target.add(Instruction.pushValue(true));
target.add(Instruction.call(3, false));
target.add(Instruction.keys(false, true));
int start = target.size();
target.add(Instruction.dup());
target.add(Instruction.call(0, false));
target.add(Instruction.dup());
target.add(Instruction.loadMember("done"));
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());
@ -57,12 +52,11 @@ public class ForInNode extends Node {
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIf(endI - mid + 1));
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
end.set(endI + 1);
LabelContext.popLoop(target.env, label);
target.add(Instruction.discard());
if (pollute) target.add(Instruction.pushUndefined());
}

View File

@ -1,17 +1,17 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class ForNode extends Node {
public final Node declaration, assignment, condition, body;

View File

@ -1,16 +1,16 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class IfNode extends Node {
public final Node condition, body, elseBody;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class ReturnNode extends Node {
public final Node value;

View File

@ -4,17 +4,17 @@ import java.util.ArrayList;
import java.util.HashMap;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class SwitchNode extends Node {
public static class SwitchCase {

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class ThrowNode extends Node {
public final Node value;

View File

@ -1,17 +1,17 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class TryNode extends Node {
public final CompoundNode tryBody;

View File

@ -1,16 +1,16 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class WhileNode extends Node {
public final Node condition, body;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.members;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.ObjectNode;
public class FieldMemberNode implements Member {

View File

@ -1,6 +1,6 @@
package me.topchetoeu.j2s.compilation.members;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
public interface Member {

View File

@ -3,16 +3,16 @@ package me.topchetoeu.j2s.compilation.members;
import java.util.Arrays;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.ObjectNode;
import me.topchetoeu.j2s.compilation.values.VariableNode;
import me.topchetoeu.j2s.compilation.values.constants.StringNode;
@ -38,20 +38,11 @@ public final class PropertyMemberNode extends FunctionNode implements Member {
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (isGetter()) {
target.add(Instruction.loadIntrinsics("defGetter"));
}
else {
target.add(Instruction.loadIntrinsics("defSetter"));
}
target.add(Instruction.dup(1, 1));
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.call(3, false));
target.add(Instruction.discard());
if (!pollute) target.add(Instruction.discard());
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) {

View File

@ -1,6 +1,6 @@
package me.topchetoeu.j2s.compilation.patterns;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
/**

View File

@ -32,7 +32,7 @@ public final class FunctionScope {
}
/**
* @return If a variable with the same name exists, the old variable. Otherwise, the given variable
* @returns If a variable with the same name exists, the old variable. Otherwise, the given variable
*/
public Variable define(Variable var) {
if (passthrough) return null;
@ -48,7 +48,7 @@ public final class FunctionScope {
}
/**
* @return A variable with the given name, or null if a global variable
* @returns A variable with the given name, or null if a global variable
*/
public Variable define(String name) {
return define(new Variable(name, false));

View File

@ -39,9 +39,9 @@ public final class VariableIndex {
}
public final Instruction toSet(boolean keep) {
switch (type) {
case CAPTURES: return Instruction.storeVar(~index, keep);
case CAPTURABLES: return Instruction.storeVar(index, keep);
case LOCALS: return Instruction.storeVar(index, keep);
case CAPTURES: return Instruction.storeVar(~index, keep, false);
case CAPTURABLES: return Instruction.storeVar(index, keep, false);
case LOCALS: return Instruction.storeVar(index, keep, false);
default: throw new UnsupportedOperationException("Unknown index type " + type);
}
}

View File

@ -175,7 +175,7 @@ public final class VariableList implements Iterable<Variable> {
this.offset = () -> offset;
}
/**
* @param prev Will offset the indices by the size of the given list
* @param offset Will offset the indices by the size of the given list
*/
public VariableList(IndexType type, VariableList prev) {
this.indexType = type;

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation.values;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -3,13 +3,13 @@ package me.topchetoeu.j2s.compilation.values;
import java.util.ArrayList;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class ArrayNode extends Node {

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation.values;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -4,16 +4,16 @@ import java.util.LinkedList;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.members.FieldMemberNode;
import me.topchetoeu.j2s.compilation.members.Member;
import me.topchetoeu.j2s.compilation.members.PropertyMemberNode;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;
import me.topchetoeu.j2s.compilation.values.constants.StringNode;

View File

@ -1,12 +1,12 @@
package me.topchetoeu.j2s.compilation.values;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class RegexNode extends Node {
public final String pattern, flags;
@ -15,10 +15,7 @@ public class RegexNode extends Node {
}
@Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadIntrinsics("regex"));
target.add(Instruction.pushValue(pattern));
target.add(Instruction.pushValue(flags));
target.add(Instruction.call(2, false));
target.add(Instruction.loadRegex(pattern, flags));
if (!pollute) target.add(Instruction.discard());
}

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation.values;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.values;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.patterns.ChangeTarget;
public class VariableNode extends Node implements ChangeTarget {

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation.values.constants;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.compilation.values.constants;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -1,12 +1,12 @@
package me.topchetoeu.j2s.compilation.values.constants;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class NumberNode extends Node {
public final double value;

View File

@ -1,12 +1,12 @@
package me.topchetoeu.j2s.compilation.values.constants;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class StringNode extends Node {
public final String value;

View File

@ -1,9 +1,9 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.patterns.AssignTarget;

View File

@ -3,14 +3,14 @@ package me.topchetoeu.j2s.compilation.values.operations;
import java.util.ArrayList;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class CallNode extends Node {
public final Node func;

View File

@ -1,14 +1,14 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.patterns.ChangeTarget;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class DiscardNode extends Node {

View File

@ -1,14 +1,14 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.patterns.ChangeTarget;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;
import me.topchetoeu.j2s.compilation.values.constants.StringNode;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class LazyAndNode extends Node {
public final Node first, second;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class LazyOrNode extends Node {

View File

@ -6,14 +6,14 @@ import java.util.Map;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.patterns.AssignTargetLike;
import me.topchetoeu.j2s.compilation.patterns.ChangeTarget;

View File

@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
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.Node;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.patterns.ChangeTarget;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;

View File

@ -1,14 +1,13 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class TypeofNode extends Node {
@ -21,14 +20,14 @@ public class TypeofNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) {
if (value instanceof VariableNode varNode) {
target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true));
if (pollute) target.add(Instruction.operation(Operation.TYPEOF));
if (pollute) target.add(Instruction.typeof());
else target.add(Instruction.discard());
return;
}
value.compile(target, pollute);
if (pollute) target.add(Instruction.operation(Operation.TYPEOF));
if (pollute) target.add(Instruction.typeof());
}
public TypeofNode(Location loc, Node value) {

View File

@ -1,8 +1,8 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.FunctionNode;
import me.topchetoeu.j2s.compilation.Node;

View File

@ -0,0 +1,14 @@
package me.topchetoeu.j2s;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestHelloWorld {
@Test
public void testHelloWorld() {
final String message = "Hello World!";
assertEquals("Hello World!", message);
}
}

View File

@ -1,98 +0,0 @@
package me.topchetoeu.j2s.compilation;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
public class TestParseRes {
@Test public void testCreateFailed() {
var res = ParseRes.failed();
assertEquals(false, res.isSuccess());
assertEquals(true, res.isFailed());
assertEquals(false, res.isError());
assertEquals(0, res.n);
assertEquals(null, res.result);
assertEquals(null, res.errorLocation);
assertEquals(null, res.error);
}
@Test public void testCreateError() {
var res = ParseRes.error(Location.of("test:10:5"), "test");
assertEquals(false, res.isSuccess());
assertEquals(false, res.isFailed());
assertEquals(true, res.isError());
assertEquals(0, res.n);
assertEquals(null, res.result);
assertEquals(Location.of("test:10:5"), res.errorLocation);
assertEquals("test", res.error);
}
@Test public void testCreateResult() {
var res = ParseRes.res("test", 10);
assertEquals(true, res.isSuccess());
assertEquals(false, res.isFailed());
assertEquals(false, res.isError());
assertEquals(10, res.n);
assertEquals("test", res.result);
assertEquals(null, res.errorLocation);
assertEquals(null, res.error);
}
@Test public void testChainFailed() {
var a = ParseRes.<Integer>failed();
var b1 = a.<String>chainError();
assertEquals(a, b1);
var b2 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(true, b2.isError());
assertEquals("test", b2.error);
}
@Test public void testChainError() {
var a = ParseRes.<Integer>error(Location.of("test:1:2"), "test");
var b1 = a.<String>chainError();
assertEquals(a, b1);
var b2 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(a, b2);
}
@Test public void testChainResult() {
var a = ParseRes.<Integer>res(10, 6);
assertThrows(RuntimeException.class, () -> a.<String>chainError());
var b1 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(true, b1.isError());
assertEquals("test", b1.error);
}
@Test public void testShouldAdd5() {
var a = ParseRes.res("test", 6);
var b = a.addN(5);
assertEquals(11, b.n);
}
@Test public void testShouldSet5() {
var a = ParseRes.res("test", 6);
var b = a.setN(5);
assertEquals(5, b.n);
}
@Test public void testShouldNotAdd() {
var a = ParseRes.failed();
var b = a.addN(5);
assertEquals(0, b.n);
}
@Test public void testShouldNotSet() {
var a = ParseRes.failed();
var b = a.setN(5);
assertEquals(0, b.n);
}
}

View File

@ -1,65 +0,0 @@
package me.topchetoeu.j2s.compilation;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class TestSource {
private Source mkSource(String src) {
return new Source(new Environment(), Filename.parse("test"), src);
}
@Test public void testShouldCreate() {
new Source(new Environment(), Filename.parse("test"), "my little source :)");
new Source(null, Filename.parse("test"), "my little source :)");
new Source("my little source :)");
}
@Test public void testShouldGet() {
var src = mkSource("1234567890");
assertEquals('1', src.at(0));
assertEquals('6', src.at(5));
}
@Test public void testShouldThrowOutOfRange() {
var src = mkSource("1234567890");
assertThrows(IndexOutOfBoundsException.class, () -> src.at(-1));
assertThrows(IndexOutOfBoundsException.class, () -> src.at(10));
}
@Test public void testImmutableSrcLoc() {
var src = mkSource("1234567890");
var loc = src.loc(5);
// kinda stupid
for (var i = 0; i < 1000; i++) {
assertEquals(5, loc.start());
assertEquals(0, loc.line());
assertEquals(Filename.parse("test"), loc.filename());
}
}
@Test public void testSingleLineSourceLocation() {
var src = mkSource("1234567890");
assertEquals(Location.of("test:1:1"), src.loc(-5));
assertEquals(Location.of("test:1:1"), src.loc(0));
assertEquals(Location.of("test:1:10"), src.loc(9));
assertEquals(Location.of("test:1:11"), src.loc(14));
}
@Test public void testMultilineSourceLocation() {
var src = mkSource("123\n456\n\n789\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
assertEquals(Location.of("test:1:1"), src.loc(-5));
assertEquals(Location.of("test:1:1"), src.loc(0));
assertEquals(Location.of("test:1:4"), src.loc(3));
assertEquals(Location.of("test:2:1"), src.loc(4));
assertEquals(Location.of("test:2:4"), src.loc(7));
assertEquals(Location.of("test:3:1"), src.loc(8));
assertEquals(Location.of("test:4:1"), src.loc(9));
assertEquals(Location.of("test:4:2"), src.loc(10));
assertEquals(Location.of("test:4:4"), src.loc(12));
assertEquals(Location.of("test:5:1"), src.loc(13));
assertEquals(Location.of("test:19:1"), src.loc(50));
}
}

View File

@ -1,65 +0,0 @@
package me.topchetoeu.j2s.compilation.parsing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class TestParseString {
@Test public void notAString() {
var res = Parsing.parseString(new Source("var a = 10"), 0);
assertEquals(true, res.isFailed());
}
@Test public void simple() {
var src = new Source("\"this is a test\"");
var res = Parsing.parseString(src, 0);
assertEquals(true, res.isSuccess());
assertEquals("this is a test", res.result);
assertEquals(16, res.n);
}
@Test public void simpleEscaped() {
var src = new Source("\'this\\\\ is \\n some \\'\\\"\\\n escapes for you :\\)\'");
var res = Parsing.parseString(src, 0);
assertEquals(true, res.isSuccess());
assertEquals("this\\ is \n some '\" escapes for you :)", res.result);
assertEquals(46, res.n);
}
@Test public void allEscaped() {
var src = new Source("'\\b\\t\\n\\f\\r\\0\\'\\x01\\u0123'");
var res = Parsing.parseString(src, 0);
assertEquals(true, res.isSuccess());
assertEquals("\b\t\n\f\r\0'\u0001\u0123", res.result);
assertEquals(26, res.n);
}
@Test public void shouldFailOctal() {
var res1 = Parsing.parseString(new Source("'\\012'"), 0);
assertEquals(true, res1.isError());
var res2 = Parsing.parseString(new Source("'\\123'"), 0);
assertEquals(true, res2.isError());
}
@Test public void shouldFailIncompleteHex() {
var res1 = Parsing.parseString(new Source("'\\x"), 0);
assertEquals(true, res1.isError());
var res2 = Parsing.parseString(new Source("'\\x1turd"), 0);
assertEquals(true, res2.isError());
var res3 = Parsing.parseString(new Source("'\\xturd"), 0);
assertEquals(true, res3.isError());
}
@Test public void shouldFailIncompleteUnicode() {
var res1 = Parsing.parseString(new Source("'\\u"), 0);
assertEquals(true, res1.isError());
var res2 = Parsing.parseString(new Source("'\\u123turd"), 0);
assertEquals(true, res2.isError());
var res3 = Parsing.parseString(new Source("'\\uturd"), 0);
assertEquals(true, res3.isError());
}
@Test public void unterminated() {
var src = new Source("\"this is a test");
var res = Parsing.parseString(src, 0);
assertEquals(true, res.isError());
}
}

View File

@ -1,48 +0,0 @@
package me.topchetoeu.j2s.compilation.parsing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class TestSkipWhite {
@Test public void shBang() {
var res1 = Parsing.skipEmpty(new Source("#!my-shbang\n10"), 0);
assertEquals(12, res1);
var res2 = Parsing.skipEmpty(new Source("#!fin"), 0);
assertEquals(5, res2);
}
@Test public void simple() {
var res1 = Parsing.skipEmpty(new Source("2134 45324"), 4);
assertEquals(3, res1);
var res2 = Parsing.skipEmpty(new Source("2134 "), 4);
assertEquals(3, res2);
}
@Test public void nothing() {
var res1 = Parsing.skipEmpty(new Source("12345678"), 4);
assertEquals(0, res1);
var res2 = Parsing.skipEmpty(new Source("1234"), 4);
assertEquals(0, res2);
}
@Test public void singleLineComment() {
var res1 = Parsing.skipEmpty(new Source("123// test\n54314214"), 3);
assertEquals(8, res1);
var res2 = Parsing.skipEmpty(new Source("123// test"), 3);
assertEquals(7, res2);
}
@Test public void multilineComment() {
var res1 = Parsing.skipEmpty(new Source("123/*test*/54314214"), 3);
assertEquals(8, res1);
var res2 = Parsing.skipEmpty(new Source("123/*test*/"), 3);
assertEquals(8, res2);
var res3 = Parsing.skipEmpty(new Source("123/*test"), 3);
assertEquals(6, res3);
}
}

9
doc/text/.gitignore vendored
View File

@ -1,9 +0,0 @@
/*
!/img
!/src
!/.gitignore
!/build
!/document.md
!/README.md
!/requirements.md
!/template.html

View File

@ -1 +0,0 @@
This is the main body of my thesis. **BE WARNED!** It is quite lengthy and written in Bulgarian.

View File

@ -1,42 +0,0 @@
#!/bin/luajit -lsrc.build
local config = require "config";
function profession(val)
if val == "sys" then
return {
profession = "481020 „Системен програмист“",
specialty = "4810201 „Системно програмиране“",
};
elseif val == "net" then
return combine {
profession = "523050 „Техник на компютърни системи“",
specialty = "5230502 „Компютърни мрежи“",
};
end
end
emit {
target = "res.html",
template {
template = "template.html",
year = os.date "%Y",
prev_year = os.date "%Y" - 1,
config,
profession(config.profession),
build {
"requirements.md",
content = "requirements",
},
build {
"document.md",
content = "content",
toc = "toc",
ctx = {
chapter_format = "Глава %s<br/>",
chapter_format_plain = "Глава %s: ",
},
},
},
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,14 +0,0 @@
---
title: Requirements
---
1. Тема: Среда за изпълнение на „EcmaScript 5“ код за виртуалната машина на „Java“
2. Изисквания
- Покритие на „ECMA-262 5.1 Edition“ стандарта
- Задоволително бързодействие
- Възможност за лесно вграждане в други софтуерни продукти
- Добри тестове, подробна документация и удобен и интуитивен публичен интерфейс за разработчици
3. Съдържание
1. Теоретична част
2. Практическа част
3. Приложение

View File

@ -1,728 +0,0 @@
---@diagnostic disable: undefined-field
require "src.utils";
local json = require "src.json";
local function reader(fd, ...)
if type(fd) == "string" then
fd = assert(io.open(fd, ... or "r"));
elseif type(fd) == "function" then
return fd;
else
assert(fd, ...);
end
return function ()
if fd == nil then error "File is closed" end
local res = fd:read(1024);
if res == nil then
fd:close();
fd = nil;
return nil;
else
return res;
end
end;
end
local function read_to_str(fd, ...)
local res = {};
for val in reader(fd, ...) do
res[#res + 1] = val;
end
return table.concat(res);
end
local flag = {};
function flag.has(name, ...)
if name == ... then
return true;
elseif ... == nil then
return false;
else
return flag.has(name, select(2, ...));
end
end
function flag.add(name, ...)
if flag.has(name, ...) then
return ...;
else
return name, ...;
end
end
local converters = {};
local function sanitize(str)
return (str
:gsub("<", "&lt")
:gsub(">", "&gt")
);
end
local function get_indent(ctx, n)
return ("\t"):rep((ctx.indent or 0) + (n or 0));
end
local function indent(ctx)
ctx.indent = (ctx.indent or 0) + 1;
end
local function undent(ctx)
ctx.indent = (ctx.indent or 1) - 1;
end
local function convert(data, ctx, ...)
local func = converters[data.t];
if func == nil then
error(table.concat { "Unknown node '", data.t, "': ", to_readable(data) });
else
return func(data.c, ctx, ...);
end
end
local function convert_all(arr, ctx, ...)
local res = array {};
local plain = array {};
local inline = true;
for i = 1, #arr do
local a, b, c = convert(arr[i], ctx, ...);
res:append(a);
plain:append(b);
if not c then
inline = false;
res:append("\n", get_indent(ctx));
end
end
return res, plain, inline;
end
local function make_id(id, readable, ctx)
ctx.ids = ctx.ids or {};
local res;
if ctx.ids[id] ~= nil then
ctx.ids[id] = ctx.ids[id] + 1;
res = id .. "-" .. ctx.ids[id];
else
ctx.ids[id] = 0;
res = id;
end
if readable then
ctx.citables = ctx.citables or {};
ctx.citables[id] = readable;
end
return res;
end
local function last_id(id, ctx)
ctx.ids = ctx.ids or {};
if ctx.ids[id] ~= nil and ctx.ids[id] > 0 then
return id .. "-" .. ctx.ids[id];
else
return id;
end
end
local function count_headers(array)
local res = 0;
for i = 1, #array do
if not array[i].ignored then
res = res + 1;
end
end
return res;
end
local function read_attributes(data, readable, ctx, no_id)
local res = {};
if data[1] ~= "" then
res.id = data[1];
end
for i = 1, #data[2] do
res[data[2][i]] = true;
end
for i = 1, #data[3] do
local curr = data[3][i];
res[curr[1]] = curr[2];
end
if not no_id and res.id then
res.id = make_id(res.id, readable, ctx);
end
return res;
end
function converters.Table(data, ctx)
local options = data[3];
local header = data[4];
local body = data[5][1];
local text = array {};
local function convert_cell(cell, tag, options)
local align = options[1].t;
indent(ctx);
text:push("<", tag);
if align == "AlignRight" then
text:push [[ class="right"]];
elseif align == "AlignLeft" then
text:push [[ class="left"]];
elseif align == "AlignCenter" then
text:push [[ class="center"]];
end
text:push(">\n", get_indent(ctx));
text:append((convert_all(cell[5], ctx)));
undent(ctx);
text:push("\n", get_indent(ctx), "</" .. tag .. ">");
end
local function convert_row(row, tag)
text:push("<tr>");
for i = 1, #row[2] do
local cell = row[2][i];
indent(ctx);
text:push("\n", get_indent(ctx));
convert_cell(cell, tag, options[i]);
undent(ctx);
end
text:push("\n", get_indent(ctx), "</tr>");
end
text:push [[<table class="graphic-table">]];
indent(ctx);
indent(ctx);
text:push("\n", get_indent(ctx, -1), "<thead>");
text:push("\n", get_indent(ctx));
convert_row(header[2][1], "th");
text:push("\n", get_indent(ctx, -1), "</thead>");
text:push("\n", get_indent(ctx, -1), "<tbody>");
for i = 1, #body[4] do
text:push("\n", get_indent(ctx));
convert_row(body[4][i], "td");
end
text:push("\n", get_indent(ctx, -1), "</tbody>");
undent(ctx);
undent(ctx);
text:push("\n", get_indent(ctx), "</table>");
return text, array { "[table]" };
end
function converters.BulletList(data, ctx)
local text = array { "<ul class=\"bullet\">" };
local plain = array {};
indent(ctx);
for i = 1, #data do
local el_text, el_plain = convert_all(data[i], ctx);
text
:push("\n", get_indent(ctx), "<li>")
:append(el_text)
:push("</li>");
plain:push("* "):append(el_plain):push("\n");
end
undent(ctx);
text:push("\n", get_indent(ctx), "</ul>");
return text, plain;
end
function converters.OrderedList(data, ctx)
local text = array { "<ol>" };
local plain = array {};
indent(ctx);
for i = 1, #data[2] do
local el_text, el_plain = convert_all(data[2][i], ctx);
text
:push("\n", get_indent(ctx), "<li value=\"", i, "\">")
:append(el_text)
:push("</li>");
plain:push(i .. ") "):append(el_plain):push("\n");
end
undent(ctx);
text:push("\n", get_indent(ctx), "</ol>");
return text, plain;
end
function converters.Code(data, ctx)
local content = data[2];
return array { "<code>", sanitize(content), "</code>" }, array { content };
end
function converters.CodeBlock(data, ctx)
local attribs = read_attributes(data[1], nil, ctx);
local content = data[2];
local text = array { "<pre><code" };
local classes = array {};
for k, v in next, attribs do
if v == true then
classes:push("language-" .. k);
end
end
if #classes > 0 then
text:push(" class=\"", classes:join " ", "\"");
end
text:push(">", sanitize(content), "</code></pre>");
return text, array { content };
end
function converters.Image(data, ctx)
local alt, alt_plain = convert_all(data[2], ctx);
local url = data[3][1];
local title = data[3][2];
local attribs = read_attributes(data[1], title or alt_plain:join " ", ctx);
local classes = array {};
if attribs.small then classes:push("small") end
if attribs.big then classes:push("big") end
local text = array {}
:push("<img title=\"", title, "\" src=\"", url, "\" alt=\"")
:append(alt)
:push("\"");
if #classes > 0 then
text:push(" class=\"", classes:join " ", "\"");
end
text:push("/>");
return text, title and { title } or alt_plain or url or "[picture]";
end
function converters.Figure(data, ctx)
local chapter_i = count_headers(ctx.headers or {});
ctx.figures_indices = ctx.figures_indices or {};
ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1;
local figure_i = ctx.figures_indices[chapter_i];
local prefix = ("%s.%s."):format(chapter_i, figure_i);
local attribs = read_attributes(data[1], prefix, ctx);
local id = attribs.id;
indent(ctx);
indent(ctx);
local name, name_plain = convert_all(data[2][1], ctx);
local content, plain = convert_all(data[3], ctx);
undent(ctx);
undent(ctx);
local text = array { "<figure" };
if id then text:push(" id=\"", id, "\"") end
text:push(">");
text
:push("\n", get_indent(ctx, 1), "<div class=\"fig-content\">", "\n", get_indent(ctx, 2))
:append(content)
:push("\n", get_indent(ctx, 1), "</div>", "\n", get_indent(ctx, 1), "<figcaption>", "\n", get_indent(ctx, 2))
:push(prefix, " ")
:append(name)
:push("\n", get_indent(ctx, 1), "</figcaption>")
:push("\n", get_indent(ctx), "</figure>");
plain
:push(" (", prefix, " ")
:append(name_plain)
:push(")");
ctx.citables = ctx.citables or {};
if id then ctx.citables[id] = prefix end
return text, plain;
end
function converters.Div(data, ctx)
local attribs = read_attributes(data[1], nil, ctx, true);
if attribs.figure then
local separator_i = data[2]:find_i(function (v) return v.t == "HorizontalRule" end);
local content_data, title_data;
if separator_i == nil then
content_data = array { data[2][1] };
title_data = data[2]:slice(2);
else
content_data = data[2]:slice(1, separator_i - 1);
title_data = data[2]:slice(separator_i + 1);
end
if #title_data == 1 and title_data[1].t == "Para" then
title_data = title_data[1].c
end
return converters.Figure(array {
data[1],
array { title_data },
content_data,
}, ctx);
else
error "Divs are not supported";
end
end
function converters.Cite(data, ctx)
local citation = data[1][1];
local id = last_id(citation.citationId, ctx);
local function target()
local res = (ctx.citables or {})[id];
if res == nil then
io.stderr:write("WARNING! Citation '" .. id .. "' doesn't exist!\n");
res = id;
end
return res;
end
return array { "<a href=\"#", id, "\">", target, "</a>" }, array { target }, true;
end
function converters.Header(data, ctx)
local level = data[1];
local attribs = read_attributes(data[2], nil, ctx);
ctx.headers = ctx.headers or array {};
ctx.header_stack = ctx.header_stack or array {};
local parent = ctx.header_stack[level - 1];
if level > 1 and parent == nil then error "Heading hierarchy inconsistent" end
local text, plain = convert_all(data[3], ctx);
local header = {
id = attribs.id,
level = level,
children = array {},
ignored = attribs.nonum or false,
};
local text_prefix, plain_prefix = "", "";
if level == 1 then
ctx.headers:push(header);
else
parent.children:push(header);
end
if not header.ignored then
local prefix = array { count_headers(ctx.headers) };
local text_format, plain_format;
for i = 1, level - 1 do
--- @diagnostic disable-next-line: undefined-field
prefix:push(count_headers(ctx.header_stack[i].children));
end
header.prefix = prefix:join ".";
if level == 1 then
text_format = ctx.chapter_format or "Chapter %s<br>";
plain_format = ctx.chapter_format_plain or "Chapter %s: ";
else
text_format = "%s. ";
plain_format = "%s. ";
end
text_prefix = text_format:format(header.prefix);
plain_prefix = plain_format:format(header.prefix);
end
ctx.header_stack = ctx.header_stack:fill(nil, level + 1, #ctx.header_stack);
ctx.header_stack[level] = header;
header.text = text_prefix .. text:join "";
header.plain = plain_prefix .. plain:join "";
if attribs.id then
ctx.citables = ctx.citables or {};
ctx.citables[attribs.id] = header.plain;
end
return
arrays.concat({ ("<h%d id=%q>"):format(level, attribs.id), text_prefix }, text, { ("</h%d>"):format(level) }),
array { plain_prefix }:append(plain);
end
function converters.Link(data, ctx)
local content, content_plain = convert_all(data[2], ctx);
local url = data[3][1];
local attribs = read_attributes(data[1], nil, ctx);
local id = attribs.id or data[3][2];
if id == "" then id = nil end
if id then
ctx.link_i = (ctx.link_i or 0) + 1;
local i = ctx.link_i;
ctx.citables = ctx.citables or {};
ctx.citables[id] = "[" .. i .. "]";
end
local plain = array {};
plain:push("<a href=\"", url, "\"");
if id ~= nil then
plain:push(" id=\"", id, "\"");
end
plain:push(">");
plain:append(content);
plain:push("</a>");
return plain, content_plain:push(" (", url, ")"), true;
end
function converters.Para(data, ctx)
indent(ctx);
local text, plain = convert_all(data, ctx);
undent(ctx);
return
array {}
:push "<p>"
:append(text)
:append "</p>",
plain;
end
function converters.Emph(data, ctx)
local text, plain = convert_all(data, ctx);
return arrays.concat({ "<i>" }, text, { "</i>" }), plain, true;
end
function converters.Strong(data, ctx)
local text, plain = convert_all(data, ctx);
return arrays.concat({ "<strong>" }, text, { "</strong>" }), plain, true;
end
function converters.Str(data)
return array { sanitize(data) }, array { data }, true;
end
function converters.Plain(data, ctx)
return convert_all(data, ctx);
end
function converters.RawBlock(data)
return array {}, array {}, true;
end
function converters.MetaInlines(data, ctx)
return convert_all(data, ctx);
end
function converters.LineBreak()
return array { "<br>" }, array { " " }, true;
end
function converters.SoftBreak()
return array { "&shy;" }, array { " " }, true;
end
function converters.HorizontalRule()
return array { "<br class=\"pg-break\"/>" }, array { " " };
end
function converters.Space()
return array { " " }, array { " " }, true;
end
local function parse_meta(meta)
local res = {};
for k, v in next, meta do
local text, plain = convert(v, {});
res[k] = text:concat "";
end
return res;
end
local function convert_headers(headers, ctx)
ctx = ctx or { indent = 0 };
indent(ctx);
local res = arrays.concat(
{ "<ul>" },
headers:flat_map(function (child)
return arrays.concat(
{
"\n", get_indent(ctx),
"<li><a href=\"#",
child.id,
"\"><span class=\"name\">",
child.plain,
"</span><span class=\"page\">Page</span></a>"
},
#child.children > 0 and convert_headers(child.children, ctx) or { "" },
{ "</li>" }
);
end),
{
"\n", get_indent(ctx, -1),
"</ul>"
}
);
undent(ctx);
return res;
end
local function parse(...)
local stream, err = io.popen("pandoc --highlight-style kate --from markdown-raw_html --to json -o - " .. array { ... }:join " ");
local str = read_to_str(stream, err);
local raw = json.parse(str);
return {
metadata = parse_meta(raw.meta),
content = raw.blocks or array {},
};
end
local function convert_page(data, ctx)
ctx = ctx or {};
ctx.headers = ctx.headers or array {};
local curr_page = array {};
local pages = array {};
for i = 1, #data.content do
local curr = data.content[i];
if curr.t == "Header" then
if curr.c[1] == 1 then
if #pages > 0 or #curr_page > 0 then
pages:append(curr_page);
end
curr_page = array {};
end
end
local text, _, inline = convert(curr, ctx);
curr_page:append(text);
if not inline then
curr_page:push("\n");
end
end
pages:append(curr_page);
local function mapper(v)
if type(v) == "function" then
return v();
else
return v;
end
end
return {
content = pages:map(mapper, true):join "",
toc = convert_headers(ctx.headers):map(mapper, true):join "",
};
end
local function subst(template, variables)
local replaces = array {};
for k, v in next, variables do
local i = 1;
local search = "{{" .. k .. "}}";
while true do
local b, e = string.find(template, search, i, true);
if b == nil then break end
replaces:push { b = b, e = e, v = v };
i = e + 1;
end
end
table.sort(replaces, function (a, b) return a.b < b.b end);
local parts = array {};
local prev = 1;
for i = 1, #replaces do
local curr = replaces[i];
parts:push(template:sub(prev, curr.b - 1), curr.v);
prev = curr.e + 1;
end
parts:push(template:sub(prev));
return parts:join "";
end
function build(options)
local toc = options.toc;
local content = options.content;
local ctx = options.ctx or {};
print(table.concat { "Building ", array(options):join ", ", "..." });
local res = convert_page(parse(unpack(options)), ctx);
if toc == nil and content == nil then
return res.content;
end
local obj = {};
if toc ~= nil then obj[toc] = res.toc end
if content ~= nil then obj[content] = res.content end
return obj;
end
function combine(objects)
local res = {};
for i = 1, #objects do
for k, v in next, objects[i] do
res[k] = v;
end
end
return res;
end
function template(options)
print("Templating into " .. options.template .. "...");
options = combine(array { options }:append(options));
return subst(read_to_str(io.open(options.template, "r")), options);
end
function emit(values)
local f = io.stdout;
if values.target then
f = assert(io.open(values.target, "w"));
print("Emitting to " .. values.target .. "...");
else
print("Emitting to stdout...");
end
for i = 1, #values do
assert(f:write(values[i]));
end
if f ~= io.stdout then
f:close();
end
end
-- return main;

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