Compare commits

..

31 Commits

Author SHA1 Message Date
f16d088646 bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 5m44s
2025-01-24 05:57:33 +02:00
8b1c2a5e4e separate more stuff into lib project 2025-01-24 05:57:15 +02:00
ee8268b144 fix: for-in not managing stack correctly 2025-01-24 05:47:44 +02:00
fffeac9bac fix: name was always 'name'
All checks were successful
tagged-release / Tagged Release (push) Successful in 4m14s
2025-01-24 04:18:35 +02:00
166e9c0470 bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 3m56s
2025-01-24 03:26:14 +02:00
6125772038 add package uploading 2025-01-24 02:45:26 +02:00
1e982cd2ef some minor restructuring 2025-01-24 00:22:15 +02:00
4389d115b6 use only babel 2025-01-24 00:22:03 +02:00
208444381e move FunctionMap logic away from core 2025-01-24 00:21:51 +02:00
eff076f6fe bump
All checks were successful
tagged-release / Tagged Release (push) Successful in 3m56s
2025-01-22 20:34:46 +02:00
24a4a01d8e fix warnings 2025-01-22 20:34:36 +02:00
b9a397a7b9 specify babel plugins separately 2025-01-22 20:34:31 +02:00
40f6cfe616 fix: make Function.compile do less stuff 2025-01-22 20:34:00 +02:00
f712fb09ae refactor: merge TYPEOF instruction into OPERATION 2025-01-22 20:32:55 +02:00
6355a48c6b fix: forEach is cringe 2025-01-22 20:21:32 +02:00
343684f9ce move more instructions as intrinsics 2025-01-22 03:57:32 +02:00
582753440b fix: remove unneeded LOAD_REGEX instruction 2025-01-22 01:54:29 +02:00
01e86b5e70 Merge pull request 'Write some tests' (#33) from topchetoeu/tests into master
Reviewed-on: #33
2025-01-15 18:23:24 +00:00
29b0c91c5d fix: function mappings were registered incorrectly 2025-01-15 20:12:52 +02:00
961c8cefcc make arguments null prototype, to follow spec closer 2025-01-15 19:54:12 +02:00
3b512b64eb move buffers out of runtime 2025-01-15 19:52:53 +02:00
0118379d4e simple func map tests 2025-01-15 19:51:35 +02:00
1be1cded9f delete placeholder tests 2025-01-15 19:51:09 +02:00
c0b23d50e5 make common project classes hierarchy flat 2025-01-12 04:31:32 +02:00
cacffd01e8 move JSON and parsing logic to compiler project, decouple JSON and parser from engine logic 2025-01-12 04:27:43 +02:00
a115843351 parseRes tests 2025-01-12 03:23:35 +02:00
3623842827 sanitize Source.loc argument 2025-01-12 03:10:44 +02:00
24d0cb73b6 don't use JSON for parsing metadata 2025-01-11 15:21:14 +02:00
7a13b032f8 add environment tests 2025-01-11 14:19:19 +02:00
5a154c8a69 filename tests 2025-01-11 13:43:02 +02:00
ae77e3b55e location tests 2025-01-11 13:42:53 +02:00
160 changed files with 2536 additions and 1697 deletions

View File

@@ -25,10 +25,14 @@ 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

@@ -45,8 +45,8 @@ env.add(Compiler.KEY, (_env, filename, raw, mapper) -> {
// We'll register the source and function source mappings for debugging
DebugContext.get(env).onSource(filename, raw);
for (var el : res.bodies()) {
DebugContext.get(env).onFunctionLoad(el, res.map(mapper));
for (var el : res.all()) {
DebugContext.get(env).onFunctionLoad(el.body(), el.map(mapper));
}
// Finally, we will construct the function

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
plugins {
id("java");
id("maven-publish");
}
version = rootProject.version;
@@ -20,3 +21,27 @@ 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.json", {
filesMatching("metadata", {
expand(
"version" to properties["project_version"],
"name" to properties["project_name"],

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.j2s.common.environment;
package me.topchetoeu.j2s.common;
import java.util.HashMap;
import java.util.HashSet;
@@ -63,8 +63,11 @@ public class Environment {
}
public <T> T init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return 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)) {

View File

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

View File

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

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

View File

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

View File

@@ -1,30 +1,58 @@
package me.topchetoeu.j2s.common;
import me.topchetoeu.j2s.common.json.JSON;
import me.topchetoeu.j2s.common.parsing.Filename;
public class Metadata {
private static final String VERSION;
private static final String AUTHOR;
private static final String NAME;
private static String VERSION;
private static String AUTHOR;
private static String NAME;
static {
var data = JSON.parse(new Filename("internal", "metadata.json"), Reading.resourceToString("metadata.json")).map();
VERSION = data.string("version");
AUTHOR = data.string("author");
NAME = data.string("name");
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));
}
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,36 +3,37 @@ package me.topchetoeu.j2s.common;
import java.util.HashMap;
public enum Operation {
INSTANCEOF(1, 2),
IN(2, 2),
TYPEOF(0x10, 1),
INSTANCEOF(0x11, 2),
IN(0x12, 2),
MULTIPLY(3, 2),
DIVIDE(4, 2),
MODULO(5, 2),
ADD(6, 2),
SUBTRACT(7, 2),
MULTIPLY(0x20, 2),
DIVIDE(0x21, 2),
MODULO(0x22, 2),
ADD(0x23, 2),
SUBTRACT(0x24, 2),
USHIFT_RIGHT(8, 2),
SHIFT_RIGHT(9, 2),
SHIFT_LEFT(10, 2),
USHIFT_RIGHT(0x30, 2),
SHIFT_RIGHT(0x31, 2),
SHIFT_LEFT(0x32, 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),
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),
AND(19, 2),
OR(20, 2),
XOR(21, 2),
AND(0x50, 2),
OR(0x51, 2),
XOR(0x52, 2),
NEG(23, 1),
POS(24, 1),
NOT(25, 1),
INVERSE(26, 1);
NEG(0x60, 1),
POS(0x61, 1),
NOT(0x62, 1),
INVERSE(0x63, 1);
private static final HashMap<Integer, Operation> operations = new HashMap<>();

View File

@@ -0,0 +1,123 @@
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,7 +1,5 @@
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,3 +0,0 @@
package me.topchetoeu.j2s.common.environment;
public final class Key<T> { }

View File

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

View File

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

View File

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

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

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

@@ -0,0 +1,159 @@
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,15 +4,6 @@ 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

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

View File

@@ -5,14 +5,13 @@ 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.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.CompilationFunctionMap.FunctionMapBuilder;
import me.topchetoeu.j2s.compilation.control.TryNode;
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.scope.Variable;
@@ -25,6 +24,7 @@ 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<>();
@@ -71,17 +71,17 @@ public final class CompileResult {
setLocationAndDebug(instructions.size() - 1, loc, type);
}
public Iterable<FunctionBody> bodies() {
var stack = new Stack<FunctionBody>();
stack.push(body());
public Iterable<CompileResult> all() {
var stack = new Stack<CompileResult>();
stack.push(this);
return () -> new Iterator<FunctionBody>() {
@Override public FunctionBody next() {
return () -> new Iterator<CompileResult>() {
@Override public CompileResult next() {
if (stack.empty()) return null;
else {
var res = stack.pop();
for (var el : res.children) {
stack.push(el);
for (var child : res.children) {
stack.push(child);
}
return res;
}
@@ -103,13 +103,15 @@ public final class CompileResult {
return instructions.toArray(new Instruction[0]);
}
public FunctionMap map(Function<Location, Location> mapper) {
public CompilationFunctionMap map(Function<Location, Location> mapper) {
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionMap map() {
public CompilationFunctionMap 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();
@@ -124,7 +126,7 @@ public final class CompileResult {
for (var instr : instrRes) System.out.println(instr);
}
return new FunctionBody(
return body = new FunctionBody(
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren
);
@@ -152,7 +154,7 @@ public final class CompileResult {
this.scope = scope;
this.instructions = new ArrayList<>();
this.children = new LinkedList<>();
this.map = FunctionMap.builder();
this.map = CompilationFunctionMap.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.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.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.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.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.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.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,13 +6,10 @@ 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;
@@ -26,6 +23,9 @@ 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.parsing.Location;
import me.topchetoeu.j2s.common.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.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.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.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.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.common.Location;
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.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.common.Location;
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,13 +31,18 @@ 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.keys(false, true));
target.add(Instruction.pushValue(false));
target.add(Instruction.pushValue(true));
target.add(Instruction.call(3, false));
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());
@@ -52,11 +57,12 @@ public class ForInNode extends Node {
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
target.set(mid, Instruction.jmpIf(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.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.common.Location;
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.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.common.Location;
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,16 +1,16 @@
package me.topchetoeu.j2s.common.json;
package me.topchetoeu.j2s.compilation.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.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;
import me.topchetoeu.j2s.compilation.parsing.ParseRes;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
public class JSON {
public static ParseRes<JSONElement> parseString(Source src, int i) {
@@ -110,37 +110,10 @@ public class JSON {
}
public static String stringify(JSONElement el) {
if (el.isNumber()) {
var d = el.number();
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (el.isNumber()) return StringifyUtils.quoteNumber(el.number());
if (el.isString()) return StringifyUtils.quoteString(el.string());
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++) {
@@ -156,7 +129,7 @@ public class JSON {
for (int i = 0; i < entries.size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
res.append(StringifyUtils.quoteString(entries.get(i).getKey()));
res.append(":");
res.append(stringify(entries.get(i).getValue()));
}

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
package me.topchetoeu.j2s.compilation.members;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.common.Location;
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.parsing.Location;
import me.topchetoeu.j2s.common.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,11 +38,20 @@ public final class PropertyMemberNode extends FunctionNode implements Member {
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (pollute) target.add(Instruction.dup());
key.compile(target, true);
if (isGetter()) {
target.add(Instruction.loadIntrinsics("defGetter"));
}
else {
target.add(Instruction.loadIntrinsics("defSetter"));
}
target.add(Instruction.dup(1, 1));
key.compile(target, true);
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(Instruction.defProp(isSetter()));
target.add(Instruction.call(3, false));
target.add(Instruction.discard());
if (!pollute) target.add(Instruction.discard());
}
public PropertyMemberNode(Location loc, Location end, Node key, VariableNode argument, CompoundNode body) {

View File

@@ -1,4 +1,6 @@
package me.topchetoeu.j2s.common.parsing;
package me.topchetoeu.j2s.compilation.parsing;
import me.topchetoeu.j2s.common.Location;
public class ParseRes<T> {
public static enum State {
@@ -33,19 +35,20 @@ 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 new ParseRes<>(state, errorLocation, error, null, 0);
return (ParseRes<T2>)this;
}
@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(); }
@@ -63,7 +66,6 @@ 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.common.parsing;
package me.topchetoeu.j2s.compilation.parsing;
public interface Parser<T> {
public ParseRes<T> parse(Source src, int i);

View File

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

View File

@@ -1,8 +1,10 @@
package me.topchetoeu.j2s.common.parsing;
package me.topchetoeu.j2s.compilation.parsing;
import java.util.function.Predicate;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
public class Source {
public final Environment env;
@@ -12,6 +14,8 @@ 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,7 +1,10 @@
package me.topchetoeu.j2s.common.parsing;
package me.topchetoeu.j2s.compilation.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,6 +1,6 @@
package me.topchetoeu.j2s.compilation.patterns;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
/**

View File

@@ -32,7 +32,7 @@ public final class FunctionScope {
}
/**
* @returns If a variable with the same name exists, the old variable. Otherwise, the given variable
* @return 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 {
}
/**
* @returns A variable with the given name, or null if a global variable
* @return 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, false);
case CAPTURABLES: return Instruction.storeVar(index, keep, false);
case LOCALS: return Instruction.storeVar(index, keep, false);
case CAPTURES: return Instruction.storeVar(~index, keep);
case CAPTURABLES: return Instruction.storeVar(index, keep);
case LOCALS: return Instruction.storeVar(index, keep);
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 offset Will offset the indices by the size of the given list
* @param prev 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.parsing.Location;
import me.topchetoeu.j2s.common.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.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.common.Location;
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.parsing.Location;
import me.topchetoeu.j2s.common.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.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.common.Location;
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.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.common.Location;
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,7 +15,10 @@ public class RegexNode extends Node {
}
@Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadRegex(pattern, flags));
target.add(Instruction.loadIntrinsics("regex"));
target.add(Instruction.pushValue(pattern));
target.add(Instruction.pushValue(flags));
target.add(Instruction.call(2, false));
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.parsing.Location;
import me.topchetoeu.j2s.common.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.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.common.Location;
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.parsing.Location;
import me.topchetoeu.j2s.common.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.parsing.Location;
import me.topchetoeu.j2s.common.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.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.common.Location;
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.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.common.Location;
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.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.common.Location;
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.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.common.Location;
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.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.common.Location;
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,13 +1,14 @@
package me.topchetoeu.j2s.compilation.values.operations;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.common.Operation;
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 {
@@ -20,14 +21,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.typeof());
if (pollute) target.add(Instruction.operation(Operation.TYPEOF));
else target.add(Instruction.discard());
return;
}
value.compile(target, pollute);
if (pollute) target.add(Instruction.typeof());
if (pollute) target.add(Instruction.operation(Operation.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

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

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

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

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

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

View File

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

View File

@@ -1,15 +1,14 @@
import com.github.gradle.node.npm.task.NpmTask;
plugins {
id("common");
id("common-java");
id("com.github.node-gradle.node") version "5.0.0";
}
tasks.compileJava {
enabled = false;
}
tasks.classes {
enabled = false;
dependencies {
implementation(project(":common"));
implementation(project(":compilation"));
implementation(project(":runtime"));
}
node {
@@ -27,16 +26,38 @@ tasks.register<NpmTask>("compileStdlib") {
args.set(listOf("run", "build-env"));
}
tasks.register<NpmTask>("compileTranspiler") {
tasks.register<NpmTask>("compileBabel") {
dependsOn("npmInstall");
inputs.files("rollup.config.js");
inputs.dir("src/transpiler");
outputs.files("build/js/transpiler.js");
outputs.files("build/js/babel.js");
// nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
args.set(listOf("run", "build-ts"));
args.set(listOf("run", "build-babel"));
}
tasks.register<NpmTask>("compileTypescript") {
dependsOn("npmInstall");
inputs.files("rollup.config.js");
inputs.dir("src/transpiler");
outputs.files("build/js/typescript.js");
// nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
args.set(listOf("run", "build-typescript"));
}
tasks.register<NpmTask>("compileCoffee") {
dependsOn("npmInstall");
inputs.files("rollup.config.js");
inputs.dir("src/transpiler");
outputs.files("build/js/coffee.js");
// nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
args.set(listOf("run", "build-coffee"));
}
tasks.jar {
@@ -50,7 +71,9 @@ tasks.jar {
tasks.processResources {
dependsOn("compileStdlib");
dependsOn("compileTranspiler");
dependsOn("compileTypescript");
dependsOn("compileBabel");
dependsOn("compileCoffee");
from("build/js") {
into("lib");
@@ -58,11 +81,4 @@ tasks.processResources {
from("src/lib") {
into("lib");
}
filesMatching("metadata.json", {
expand(
"version" to properties["project_version"].toString(),
"name" to properties["project_name"].toString(),
);
})
}

View File

@@ -1,7 +1,9 @@
{
"scripts": {
"build-env": "rollup -c --environment INPUT:src/stdlib/_entry.ts,OUTPUT:build/js/stdlib.js,POLYFILLS:src/polyfills",
"build-ts": "rollup -c --environment INPUT:src/transpiler/_entry.ts,OUTPUT:build/js/transpiler.js"
"build-babel": "rollup -c --environment INPUT:src/transpiler/_entry-babel.ts,OUTPUT:build/js/babel.js",
"build-coffee": "rollup -c --environment INPUT:src/transpiler/_entry-coffee.ts,OUTPUT:build/js/coffee.js",
"build-typescript": "rollup -c --environment INPUT:src/transpiler/_entry-typescript.ts,OUTPUT:build/js/typescript.js"
},
"dependencies": {
"@babel/core": "^7.26.0",

View File

@@ -10,6 +10,8 @@ declare interface String {
charCodeAt(i: number): number;
codePointAt(i: number): number;
startsWith(search: string): boolean;
endsWith(search: string): boolean;
includes(search: string, offset?: number): number;
indexOf(search: string, offset?: number): number;
lastIndexOf(search: string, offset?: number): number;

View File

@@ -0,0 +1,123 @@
package me.topchetoeu.j2s.lib;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.Reading;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.runtime.Compiler;
import me.topchetoeu.j2s.runtime.debug.DebugHandler;
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
import me.topchetoeu.j2s.runtime.values.functions.NativeFunction;
import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
public class Compilers {
public static Compiler jsCompiler() {
return (env, filename, raw, mapper) -> {
try {
var res = JavaScript.compile(env, filename, raw, true);
var body = res.body();
DebugHandler.get(env).onSourceLoad(filename, raw);
for (var el : res.all()) {
DebugHandler.get(env).onFunctionLoad(el.body(), el.map(mapper));
}
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
}
catch (SyntaxException e) {
var res = EngineException.ofSyntax(e.msg);
res.add(env, e.loc.filename() + "", e.loc);
throw res;
}
};
}
public static Compiler wrap(Compiler first, Environment compilerEnv, Environment targetEnv, FunctionValue factory) {
var curr = new NativeFunction(args -> {
var filename = Filename.parse(args.get(0).toString(args.env));
var src = args.get(1).toString(args.env);
var mapper = (FunctionValue)args.get(2);
return first.compile(targetEnv, filename, src, NativeMapper.unwrap(args.env, mapper));
});
var next = (FunctionValue)factory.apply(compilerEnv, Value.UNDEFINED, curr);
return (env, filename, source, map) -> {
return (FunctionValue)next.apply(
compilerEnv, Value.UNDEFINED,
StringValue.of(filename.toString()),
StringValue.of(source),
new NativeMapper(map)
);
};
}
public static Compiler transpilerFromSource(Compiler prev, Environment target, Filename compilerName, String compilerSrc) {
var env = StdLib.apply(null);
var glob = Value.global(env);
var compilerFactory = new FunctionValue[1];
glob.defineOwnField(env, "getResource", new NativeFunction(args -> {
var name = args.get(0).toString(args.env);
var src = Reading.resourceToString("lib/" + name);
if (src == null) return Value.UNDEFINED;
else return StringValue.of(src);
}));
glob.defineOwnField(env, "register", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
compilerFactory[0] = func;
return Value.UNDEFINED;
}));
glob.defineOwnField(env, "registerSource", new NativeFunction(args -> {
var filename = Filename.parse(args.get(0).toString(args.env));
var src = args.get(1).toString(args.env);
DebugHandler.get(target).onSourceLoad(filename, src);
return Value.UNDEFINED;
}));
var compiled = JavaScript.compile(compilerName, compilerSrc, false);
new CodeFunction(env, "intializer", compiled.body(), new Value[0][]).apply(env, Value.UNDEFINED);
return wrap(prev, env, target, compilerFactory[0]);
}
public static Compiler babelCompiler(Compiler prev, Environment target) {
return transpilerFromSource(prev, target,
new Filename(Metadata.name(), "babel.js"),
Reading.resourceToString("lib/babel.js")
);
}
public static Compiler typescriptCompiler(Compiler prev, Environment target) {
return transpilerFromSource(prev, target,
new Filename(Metadata.name(), "typescript.js"),
Reading.resourceToString("lib/typescript.js")
);
}
public static Compiler coffeescriptCompiler(Compiler prev, Environment target) {
return transpilerFromSource(prev, target,
new Filename(Metadata.name(), "coffee.js"),
Reading.resourceToString("lib/coffee.js")
);
}
public static interface TranspilerFactory {
Compiler create(Compiler prev, Environment target);
}
public static Compiler chainTranspilers(Environment target, Compiler base, TranspilerFactory ...factories) {
var res = base;
for (var el : factories) {
res = el.create(res, target);
}
return res;
}
}

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.j2s.runtime;
package me.topchetoeu.j2s.lib;
import java.util.HashSet;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.json.JSONElement;
import me.topchetoeu.j2s.common.json.JSONList;
import me.topchetoeu.j2s.common.json.JSONMap;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.compilation.json.JSONElement;
import me.topchetoeu.j2s.compilation.json.JSONList;
import me.topchetoeu.j2s.compilation.json.JSONMap;
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.objects.ArrayValue;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.j2s.repl.mapping;
package me.topchetoeu.j2s.lib;
import java.util.function.Function;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Location;
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;

View File

@@ -0,0 +1,694 @@
package me.topchetoeu.j2s.lib;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import me.topchetoeu.j2s.runtime.Compiler;
import me.topchetoeu.j2s.runtime.EventLoop;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Key;
import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.compilation.json.JSON;
import me.topchetoeu.j2s.compilation.parsing.Parsing;
import me.topchetoeu.j2s.compilation.parsing.Source;
import me.topchetoeu.j2s.lib.buffers.Int32ArrayValue;
import me.topchetoeu.j2s.lib.buffers.Int8ArrayValue;
import me.topchetoeu.j2s.lib.buffers.TypedArrayValue;
import me.topchetoeu.j2s.lib.buffers.Uint8ArrayValue;
import me.topchetoeu.j2s.runtime.ArgumentsValue;
import me.topchetoeu.j2s.runtime.Frame;
import me.topchetoeu.j2s.runtime.exceptions.EngineException;
import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.FunctionValue;
import me.topchetoeu.j2s.runtime.values.functions.NativeFunction;
import me.topchetoeu.j2s.runtime.values.objects.ArrayLikeValue;
import me.topchetoeu.j2s.runtime.values.objects.ArrayValue;
import me.topchetoeu.j2s.runtime.values.objects.ObjectValue;
import me.topchetoeu.j2s.runtime.values.primitives.BoolValue;
import me.topchetoeu.j2s.runtime.values.primitives.StringValue;
import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue;
import me.topchetoeu.j2s.runtime.values.primitives.UserValue;
import me.topchetoeu.j2s.runtime.values.primitives.VoidValue;
import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue;
public class Primordials {
@SuppressWarnings("unchecked")
public static ObjectValue mapPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
var prototype = new ObjectValue[1];
NativeFunction mapConstr = new NativeFunction(args -> {
var isWeak = args.get(0).toBoolean();
return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]);
});
mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> {
var map = getArgs.self(Map.class);
var key = getArgs.get(0);
var val = map.get(key);
return val == null ? Value.UNDEFINED : (Value)val;
}));
mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> {
var map = getArgs.self(Map.class);
var key = getArgs.get(0);
var val = getArgs.get(1);
map.put(key, val);
return Value.UNDEFINED;
}));
mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> {
var map = getArgs.self(Map.class);
var key = getArgs.get(0);
return BoolValue.of(map.containsKey(key));
}));
mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> {
var map = getArgs.self(Map.class);
var key = getArgs.get(0);
map.remove(key);
return Value.UNDEFINED;
}));
mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> {
var map = getArgs.self(Map.class);
return ArrayValue.of(map.keySet());
}));
mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> {
getArgs.self(Map.class).clear();
return Value.UNDEFINED;
}));
mapConstr.prototype.defineOwnField(env, "size", new NativeFunction(getArgs -> {
return NumberValue.of(getArgs.self(Map.class).size());
}));
prototype[0] = (ObjectValue)mapConstr.prototype;
return mapConstr;
}
public static String processRegex(String src) {
var n = 0;
var source = new StringBuilder();
StringBuilder bracesSource = null;
StringBuilder bracketsSource = null;
while (true) {
if (n >= src.length()) break;
var c = src.charAt(n++);
if (c == '\\' && n + 1 < src.length() && src.charAt(n) == 'b') {
c = '\b';
n++;
}
if (bracesSource != null) {
var failed = true;
if (Character.isDigit(c)) {
bracesSource.append(c);
failed = false;
}
else if (c == ',' && bracesSource.indexOf(",") < 0) {
bracesSource.append(c);
failed = false;
}
else if (c == '}' && bracesSource.length() > 0) {
bracesSource.append(c);
source.append(bracesSource);
bracesSource = null;
continue;
}
if (failed) {
source.append("\\");
source.append(bracesSource);
bracesSource = null;
n--;
}
}
else if (bracketsSource != null) {
if (c == '[') bracketsSource.append("\\[");
else if (c == ']') {
var res = bracketsSource.append(']').toString();
bracketsSource = null;
if (res.equals("[^]")) res = "[\\s\\S]";
else if (res.equals("[]")) res = "[^\\s\\S]";
source.append(res);
}
else if (c == '\\') {
if (n >= src.length()) break;
bracketsSource.append(c).append(src.charAt(n++));
}
else bracketsSource.append(c);
}
else if (c == '\\') {
if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n);
c = src.charAt(n++);
source.append('\\').append(c);
}
else if (c == '[') {
bracketsSource = new StringBuilder("[");
}
else if (c == '{' && bracketsSource == null) {
bracesSource = new StringBuilder("{");
}
else source.append(c);
}
if (bracesSource != null) {
source.append("\\");
source.append(bracesSource);
}
if (bracketsSource != null) throw new PatternSyntaxException("Unmatched '['", src, n - bracketsSource.length());
return source.toString();
}
public static ObjectValue regexPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
var prototype = new ObjectValue[1];
NativeFunction mapConstr = new NativeFunction(args -> {
var flags = 0;
if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE;
if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE;
if (args.get(3).toBoolean()) flags |= Pattern.DOTALL;
if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ;
if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS;
try {
var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags);
return UserValue.of(pattern, prototype[0]);
}
catch (PatternSyntaxException e) {
throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription());
}
});
mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> {
var pattern = args.self(Pattern.class);
var target = args.get(0).toString(args.env);
var offset = args.get(1).toNumber(args.env).getInt();
var index = args.get(2).toBoolean();
if (offset > target.length()) return Value.NULL;
var matcher = pattern.matcher(target).region(offset, target.length());
if (!matcher.find()) return Value.NULL;
var matchesArr = new ArrayValue(matcher.groupCount() + 1);
for (var i = 0; i < matcher.groupCount() + 1; i++) {
var group = matcher.group(i);
if (group == null) continue;
matchesArr.set(args.env, i, StringValue.of(group));
}
matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start()));
matchesArr.defineOwnField(args.env, "input", StringValue.of(target));
if (index) {
var indices = new ArrayValue();
indices.setPrototype(args.env, null);
for (var i = 0; i < matcher.groupCount(); i++) {
matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList(
NumberValue.of(matcher.start(i)),
NumberValue.of(matcher.end(i))
)));
}
}
var obj = new ObjectValue();
obj.defineOwnField(args.env, "matches", matchesArr);
obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end()));
return obj;
// return val == null ? Value.UNDEFINED : (Value)val;
}));
mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> {
var pattern = args.self(Pattern.class);
return NumberValue.of(pattern.matcher("").groupCount());
}));
prototype[0] = (ObjectValue)mapConstr.prototype;
return mapConstr;
}
public static ObjectValue symbolPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env))));
res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env))));
res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key()));
res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value)));
return res;
}
public static ObjectValue numberPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "parseInt", new NativeFunction(args -> {
var nradix = args.get(1).toNumber(env);
var radix = nradix.isInt() ? nradix.getInt() : 10;
if (radix != 10 && args.get(0) instanceof NumberValue num) {
if (num.isInt()) return num;
else return NumberValue.of(num.getDouble() - num.getDouble() % 1);
}
else {
if (radix < 2 || radix > 36) return NumberValue.NAN;
var str = args.get(0).toString().trim();
var numRes = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
if (numRes.isSuccess()) {
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
}
return NumberValue.NAN;
}
}));
res.defineOwnField(env, "parseFloat", new NativeFunction(args -> {
if (args.get(0) instanceof NumberValue) {
return args.get(0);
}
else {
var str = args.get(0).toString().trim();
var numRes = Parsing.parseFloat(new Source(str), 0, true);
if (numRes.isSuccess()) {
if (numRes.n == str.length()) return NumberValue.of(numRes.result);
}
return NumberValue.NAN;
}
}));
res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN())));
res.defineOwnField(env, "pow", new NativeFunction(args -> {
return NumberValue.of(Math.pow(args.get(0).toNumber(args.env).getDouble(), args.get(1).toNumber(args.env).getDouble()));
}));
res.defineOwnField(env, "log", new NativeFunction(args -> {
return NumberValue.of(Math.log(args.get(0).toNumber(args.env).getDouble()));
}));
res.defineOwnField(env, "NaN", NumberValue.NAN);
res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY));
res.defineOwnField(env, "PI", NumberValue.of(Math.PI));
res.defineOwnField(env, "E", NumberValue.of(Math.E));
return res;
}
public static ObjectValue stringPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "stringBuild", new NativeFunction(args -> {
var parts = ((ArrayValue)args.get(0)).toArray();
var sb = new StringBuilder();
for (var i = 0; i < parts.length; i++) {
sb.append(((StringValue)parts[i]).value);
}
return StringValue.of(sb.toString());
}));
res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> {
return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() }));
}));
res.defineOwnField(env, "toCharCode", new NativeFunction(args -> {
return NumberValue.of(args.get(0).toString(args.env).charAt(0));
}));
res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> {
return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt()));
}));
res.defineOwnField(env, "substring", new NativeFunction(args -> {
var str = args.get(0).toString(args.env);
var start = args.get(1).toNumber(args.env).getInt();
var end = args.get(2).toNumber(args.env).getInt();
if (end <= start) return StringValue.of("");
start = Math.max(Math.min(start, str.length()), 0);
end = Math.max(Math.min(end, str.length()), 0);
return StringValue.of(str.substring(start, end));
}));
res.defineOwnField(env, "indexOf", new NativeFunction(args -> {
var str = args.get(0).toString(args.env);
var search = args.get(1).toString(args.env);
var start = args.get(2).toNumber(args.env).getInt();
if (start > str.length()) return NumberValue.of(-1);
var reverse = args.get(3).toBoolean();
if (reverse) return NumberValue.of(str.lastIndexOf(search, start));
else return NumberValue.of(str.indexOf(search, start));
}));
res.defineOwnField(env, "lower", new NativeFunction(args -> {
return StringValue.of(args.get(0).toString(args.env).toLowerCase());
}));
res.defineOwnField(env, "upper", new NativeFunction(args -> {
return StringValue.of(args.get(0).toString(args.env).toUpperCase());
}));
return res;
}
public static ObjectValue objectPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "defineField", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0);
var key = args.get(1);
var desc = (ObjectValue)args.get(2);
var valField = desc.getOwnMember(env, "v");
var writeField = desc.getOwnMember(env, "w");
var configField = desc.getOwnMember(env, "c");
var enumField = desc.getOwnMember(env, "e");
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
var writable = writeField == null ? null : writeField.get(env, desc).toBoolean();
var value = valField == null ? null : valField.get(env, desc);
return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable));
}));
res.defineOwnField(env, "defineProperty", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0);
var key = args.get(1);
var desc = args.get(2);
var configField = desc.getOwnMember(env, "c");
var enumField = desc.getOwnMember(env, "e");
var getField = desc.getOwnMember(env, "g");
var setField = desc.getOwnMember(env, "s");
var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean();
var configurable = configField == null ? null : configField.get(env, desc).toBoolean();
Optional<FunctionValue> getter = null, setter = null;
if (getField != null) {
var getVal = getField.get(env, desc);
if (getVal == Value.UNDEFINED) getter = Optional.empty();
else getter = Optional.of((FunctionValue)getVal);
}
if (setField != null) {
var setVal = setField.get(env, desc);
if (setVal == Value.UNDEFINED) setter = Optional.empty();
else setter = Optional.of((FunctionValue)setVal);
}
return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable));
}));
res.defineOwnField(env, "getPrototype", new NativeFunction(args -> {
var proto = args.get(0).getPrototype(env);
if (proto == null) return Value.NULL;
else return proto;
}));
res.defineOwnField(env, "setPrototype", new NativeFunction(args -> {
var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1);
args.get(0).setPrototype(env, proto);
return args.get(0);
}));
res.defineOwnField(env, "getMembers", new NativeFunction(args -> {
var val = new ArrayValue();
for (var key : args.get(0).getMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())) {
val.set(args.env, val.size(), StringValue.of(key));
}
return val;
}));
res.defineOwnField(env, "getSymbolMembers", new NativeFunction(args -> {
return ArrayValue.of(args.get(0).getSymbolMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean()));
}));
res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> {
var obj = args.get(0);
var key = args.get(1);
var member = obj.getOwnMember(args.env, key);
if (member == null) return Value.UNDEFINED;
else return member.descriptor(args.env, obj);
}));
res.defineOwnField(env, "preventExt", new NativeFunction(args -> {
args.get(0).preventExtensions();
return VoidValue.UNDEFINED;
}));
res.defineOwnField(env, "seal", new NativeFunction(args -> {
args.get(0).seal();
return VoidValue.UNDEFINED;
}));
res.defineOwnField(env, "freeze", new NativeFunction(args -> {
args.get(0).freeze();
return VoidValue.UNDEFINED;
}));
res.defineOwnField(env, "memcpy", new NativeFunction(args -> {
var src = (ArrayValue)args.get(0);
var dst = (ArrayValue)args.get(1);
var srcI = args.get(2).toNumber(args.env).getInt();
var dstI = args.get(3).toNumber(args.env).getInt();
var n = args.get(4).toNumber(args.env).getInt();
src.copyTo(dst, srcI, dstI, n);
return VoidValue.UNDEFINED;
}));
res.defineOwnField(env, "sort", new NativeFunction(args -> {
var arr = (ArrayValue)args.get(0);
var func = (FunctionValue)args.get(1);
arr.sort((a, b) -> {
return func.apply(args.env, Value.UNDEFINED, a, b).toNumber(args.env).getInt();
});
return arr;
}));
res.defineOwnField(env, "isArray", new NativeFunction(args -> {
return BoolValue.of(args.get(0) instanceof ArrayLikeValue);
}));
return res;
}
public static ObjectValue bufferPrimordials(Environment env) {
var buffProto = new ObjectValue();
buffProto.defineOwnProperty(env, "length", Optional.of(new NativeFunction(args -> {
return NumberValue.of(args.self(byte[].class).length);
})), Optional.empty(), false, true);
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "buff", new NativeFunction(args -> {
var size = args.get(0).toNumber(env).getInt();
return TypedArrayValue.buffer(new byte[size], buffProto);
}));
res.defineOwnField(env, "uint8", new NativeFunction(args -> {
var buff = args.get(byte[].class, 0);
var start = args.get(1).toNumber(env).getInt();
var end = args.get(2).toNumber(env).getInt();
return new Uint8ArrayValue(buff, start, end);
}));
res.defineOwnField(env, "int8", new NativeFunction(args -> {
var buff = args.get(byte[].class, 0);
var start = args.get(1).toNumber(env).getInt();
var end = args.get(2).toNumber(env).getInt();
return new Int8ArrayValue(buff, start, end);
}));
res.defineOwnField(env, "int32", new NativeFunction(args -> {
var buff = args.get(byte[].class, 0);
var start = args.get(1).toNumber(env).getInt();
var end = args.get(2).toNumber(env).getInt();
return new Int32ArrayValue(buff, start, end);
}));
res.defineOwnField(env, "isUint8", new NativeFunction(args -> {
return BoolValue.of(args.get(0) instanceof Uint8ArrayValue);
}));
res.defineOwnField(env, "isInt8", new NativeFunction(args -> {
return BoolValue.of(args.get(0) instanceof Int8ArrayValue);
}));
res.defineOwnField(env, "isInt32", new NativeFunction(args -> {
return BoolValue.of(args.get(0) instanceof Int32ArrayValue);
}));
res.defineOwnField(env, "is", new NativeFunction(args -> {
return BoolValue.of(args.get(0) instanceof TypedArrayValue);
}));
res.defineOwnField(env, "isBuff", new NativeFunction(args -> {
return BoolValue.of(args.get(byte[].class, 0) != null);
}));
res.defineOwnField(env, "backer", new NativeFunction(args -> {
return TypedArrayValue.buffer(((TypedArrayValue)args.get(0)).buffer, buffProto);
}));
res.defineOwnField(env, "start", new NativeFunction(args -> {
return NumberValue.of(((TypedArrayValue)args.get(0)).start);
}));
res.defineOwnField(env, "end", new NativeFunction(args -> {
return NumberValue.of(((TypedArrayValue)args.get(0)).end);
}));
return res;
}
public static ObjectValue functionPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "setCallable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
func.enableApply = args.get(1).toBoolean();
return Value.UNDEFINED;
}));
res.defineOwnField(env, "setConstructable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
func.enableConstruct = args.get(1).toBoolean();
return Value.UNDEFINED;
}));
res.defineOwnField(env, "invokeType", new NativeFunction(args -> {
if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
else return StringValue.of("call");
}));
res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> {
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
if (frame.isNew) return StringValue.of("new");
else return StringValue.of("call");
}));
res.defineOwnField(env, "target", new NativeFunction(args -> {
var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt());
if (frame.target == null) return Value.UNDEFINED;
else return frame.target;
}));
res.defineOwnField(env, "invoke", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
var self = args.get(1);
var funcArgs = (ArrayLikeValue)args.get(2);
return func.apply(env, self, funcArgs.toArray());
}));
res.defineOwnField(env, "construct", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
var target = args.get(1);
var funcArgs = (ArrayLikeValue)args.get(2);
if (target == Value.UNDEFINED) return func.constructNoSelf(env, funcArgs.toArray());
else return func.construct(env, target, funcArgs.toArray());
}));
return res;
}
public static ObjectValue jsonPrimordials(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "stringify", new NativeFunction(args -> {
return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0))));
}));
res.defineOwnField(env, "parse", new NativeFunction(args -> {
try {
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
}
catch (SyntaxException e) {
throw EngineException.ofSyntax(e.msg).add(env, e.loc.filename() + "", e.loc);
}
}));
return res;
}
public static void setProto(Environment env, Environment target, Key<ObjectValue> key, ObjectValue repo, String name) {
var val = repo.getMember(env, name);
if (val instanceof ObjectValue obj) {
target.add(key, obj);
}
}
public static ObjectValue create(Environment env) {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnField(env, "symbol", symbolPrimordials(env));
res.defineOwnField(env, "number", numberPrimordials(env));
res.defineOwnField(env, "string", stringPrimordials(env));
res.defineOwnField(env, "object", objectPrimordials(env));
res.defineOwnField(env, "buffer", bufferPrimordials(env));
res.defineOwnField(env, "function", functionPrimordials(env));
res.defineOwnField(env, "json", jsonPrimordials(env));
res.defineOwnField(env, "map", mapPrimordials(env));
res.defineOwnField(env, "regex", regexPrimordials(env));
int[] i = new int[1];
res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(0);
setProto(args.env, env, Value.OBJECT_PROTO, obj, "object");
setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function");
setProto(args.env, env, Value.ARRAY_PROTO, obj, "array");
setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean");
setProto(args.env, env, Value.NUMBER_PROTO, obj, "number");
setProto(args.env, env, Value.STRING_PROTO, obj, "string");
setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol");
setProto(args.env, env, Value.ERROR_PROTO, obj, "error");
setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax");
setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type");
setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range");
setProto(args.env, env, Value.UINT8_ARR_PROTO, obj, "uint8");
setProto(args.env, env, Value.INT32_ARR_PROTO, obj, "int32");
return Value.UNDEFINED;
}));
res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> {
var name = args.get(0).toString(env);
var val = args.get(1);
Value.intrinsics(env).put(name, val);
return Value.UNDEFINED;
}));
res.defineOwnField(env, "compile", new NativeFunction(args -> {
var nameVal = args.get(1);
var name = nameVal instanceof VoidValue ?
new Filename(Metadata.name(), "func" + i[0]++ + ".js") :
Filename.parse(nameVal.toString(args.env));
return Compiler.compileFunc(env, name, args.get(0).toString(env));
}));
res.defineOwnField(env, "now", new NativeFunction(args -> {
return NumberValue.of(System.currentTimeMillis());
}));
res.defineOwnField(env, "next", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
EventLoop.get(env).pushMsg(() -> {
func.apply(env, Value.UNDEFINED);
}, true);
return Value.UNDEFINED;
}));
res.defineOwnField(env, "print", new NativeFunction(args -> {
for (var el : args.args) {
if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t");
else System.out.print(el.toReadable(args.env) + " \t");
}
System.out.println();
return Value.UNDEFINED;
}));
return res;
}
}

View File

@@ -0,0 +1,29 @@
package me.topchetoeu.j2s.lib;
import me.topchetoeu.j2s.common.Environment;
import me.topchetoeu.j2s.common.Filename;
import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.Reading;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.runtime.values.Value;
import me.topchetoeu.j2s.runtime.values.functions.CodeFunction;
public class StdLib {
private static final CompileResult RUNNER = JavaScript.compile(new Filename(Metadata.name(), "init.js"), Reading.resourceToString("lib/stdlib.js"), false);
public static Environment apply(Environment env) {
if (env == null) {
env = new Environment();
}
var stubEnv = new Environment();
Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env));
Value.global(stubEnv).defineOwnField(stubEnv, "primordials", Primordials.create(env));
var func = new CodeFunction(stubEnv, "intializer", RUNNER.body(), new Value[0][]);
func.apply(stubEnv, Value.UNDEFINED);
return env;
}
}

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