refactor: clean up REPL stringification code
This commit is contained in:
parent
fdac93bf4d
commit
59e6f34a01
@ -3,27 +3,25 @@ package me.topchetoeu.jscript.runtime.values;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.environment.Key;
|
||||
import me.topchetoeu.jscript.common.json.JSON;
|
||||
import me.topchetoeu.jscript.common.json.JSONElement;
|
||||
import me.topchetoeu.jscript.runtime.EventLoop;
|
||||
import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.PropertyMember;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.NativeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ArrayValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.BoolValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
|
||||
@ -429,105 +427,13 @@ public abstract class Value {
|
||||
}
|
||||
}
|
||||
|
||||
private final String toReadable(Environment env, HashSet<Object> passed, int tab) {
|
||||
if (passed.contains(this)) return "[circular]";
|
||||
|
||||
if (this instanceof ObjectValue obj) {
|
||||
var res = new StringBuilder();
|
||||
var dbg = DebugContext.get(env);
|
||||
var printed = true;
|
||||
var keys = this.getMembers(env, true, false);
|
||||
|
||||
if (this instanceof FunctionValue func) {
|
||||
res.append(this.toString());
|
||||
var loc = dbg.getMapOrEmpty(func).start();
|
||||
|
||||
if (loc != null) res.append(" @ " + loc);
|
||||
|
||||
if (
|
||||
func.prototype instanceof ObjectValue objProto &&
|
||||
objProto.getMember(env, "constructor") == func &&
|
||||
objProto.getOwnMembers(env, true).size() + objProto.getOwnSymbolMembers(env, true).size() == 1
|
||||
) { keys.remove("constructor"); }
|
||||
}
|
||||
else if (this instanceof ArrayValue) {
|
||||
res.append("[");
|
||||
var arr = (ArrayValue)this;
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (i != 0) res.append(", ");
|
||||
else res.append(" ");
|
||||
|
||||
if (arr.hasMember(env, i, true)) {
|
||||
res.append(arr.getMember(env, i).toReadable(env, passed, tab));
|
||||
keys.remove(i + "");
|
||||
}
|
||||
else res.append("<empty>");
|
||||
}
|
||||
|
||||
res.append(" ] ");
|
||||
}
|
||||
else printed = false;
|
||||
|
||||
|
||||
passed.add(this);
|
||||
|
||||
if (keys.size() + obj.getOwnSymbolMembers(env, true).size() == 0) {
|
||||
if (!printed) res.append("{}");
|
||||
}
|
||||
else if (!printed) {
|
||||
if (tab > 3) return "{...}";
|
||||
res.append("{\n");
|
||||
|
||||
for (var entry : obj.getOwnSymbolMembers(env, true)) {
|
||||
for (int i = 0; i < tab + 1; i++) res.append(" ");
|
||||
res.append("[" + entry.value + "]" + ": ");
|
||||
|
||||
var member = obj.getOwnMember(env, entry);
|
||||
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1));
|
||||
else if (member instanceof PropertyMember prop) {
|
||||
if (prop.getter == null && prop.setter == null) res.append("[No accessors]");
|
||||
else if (prop.getter == null) res.append("[Setter]");
|
||||
else if (prop.setter == null) res.append("[Getter]");
|
||||
else res.append("[Getter/Setter]");
|
||||
}
|
||||
else res.append("[???]");
|
||||
|
||||
res.append(",\n");
|
||||
}
|
||||
for (var entry : obj.getOwnMembers(env, true)) {
|
||||
for (int i = 0; i < tab + 1; i++) res.append(" ");
|
||||
res.append(entry + ": ");
|
||||
|
||||
var member = obj.getOwnMember(env, entry);
|
||||
if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1));
|
||||
else if (member instanceof PropertyMember prop) {
|
||||
if (prop.getter == null && prop.setter == null) res.append("[No accessors]");
|
||||
else if (prop.getter == null) res.append("[Setter]");
|
||||
else if (prop.setter == null) res.append("[Getter]");
|
||||
else res.append("[Getter/Setter]");
|
||||
}
|
||||
else res.append("[???]");
|
||||
|
||||
res.append(",\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < tab; i++) res.append(" ");
|
||||
res.append("}");
|
||||
}
|
||||
|
||||
passed.remove(this);
|
||||
return res.toString();
|
||||
}
|
||||
else if (this instanceof VoidValue) return ((VoidValue)this).name;
|
||||
else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value));
|
||||
else if (this instanceof SymbolValue) return this.toString();
|
||||
else if (this instanceof NumberValue num && num.isLong()) return num.getLong() + "i";
|
||||
else return this.toString(env);
|
||||
/** @internal */
|
||||
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(toString(env));
|
||||
}
|
||||
|
||||
public final String toReadable(Environment ext) {
|
||||
return toReadable(ext, new HashSet<>(), 0);
|
||||
return String.join("\n", toReadableLines(ext, new HashSet<>()));
|
||||
}
|
||||
|
||||
public static final ObjectValue global(Environment env) {
|
||||
|
@ -1,6 +1,12 @@
|
||||
package me.topchetoeu.jscript.runtime.values.functions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.debug.DebugContext;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
@ -83,6 +89,20 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
|
||||
@Override public StringValue type() { return StringValue.of("function"); }
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
var dbg = DebugContext.get(env);
|
||||
var res = new StringBuilder(this.toString());
|
||||
var loc = dbg.getMapOrEmpty(this).start();
|
||||
|
||||
if (loc != null) res.append(" @ " + loc);
|
||||
|
||||
var lines = new LinkedList<String>(super.toReadableLines(env, passed));
|
||||
if (lines.size() == 1 && lines.getFirst().equals("{}")) return Arrays.asList(res.toString());
|
||||
lines.set(0, res.toString() + " " + lines.getFirst());
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void setName(String val) {
|
||||
if (this.name == null || this.name.equals("")) this.name = val;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package me.topchetoeu.jscript.runtime.values.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
@ -104,4 +108,90 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private LinkedList<String> toReadableBase(Environment env, HashSet<ObjectValue> passed, HashSet<String> ignoredKeys) {
|
||||
var stringified = new LinkedList<LinkedList<String>>();
|
||||
|
||||
passed.add(this);
|
||||
|
||||
var emptyN = 0;
|
||||
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (has(i)) {
|
||||
String emptyStr = null;
|
||||
|
||||
if (emptyN == 1) emptyStr = "<empty>";
|
||||
else if (emptyN > 1) emptyStr = "<empty x " + emptyN + ">";
|
||||
|
||||
if (emptyStr != null) stringified.add(new LinkedList<>(Arrays.asList(emptyStr + ",")));
|
||||
emptyN = 0;
|
||||
|
||||
stringified.add(new LinkedList<>(get(i).toReadableLines(env, passed)));
|
||||
ignoredKeys.add(i + "");
|
||||
|
||||
var entry = stringified.getLast();
|
||||
entry.set(entry.size() - 1, entry.getLast() + ",");
|
||||
}
|
||||
else {
|
||||
emptyN++;
|
||||
}
|
||||
}
|
||||
|
||||
String emptyStr = null;
|
||||
|
||||
if (emptyN == 1) emptyStr = "<empty>";
|
||||
else if (emptyN > 1) emptyStr = "<empty x " + emptyN + ">";
|
||||
|
||||
if (emptyStr != null) stringified.add(new LinkedList<>(Arrays.asList(emptyStr)));
|
||||
else if (stringified.size() > 0) {
|
||||
var lastEntry = stringified.getLast();
|
||||
lastEntry.set(lastEntry.size() - 1, lastEntry.getLast().substring(0, lastEntry.getLast().length() - 1));
|
||||
}
|
||||
|
||||
|
||||
passed.remove(this);
|
||||
|
||||
if (stringified.size() == 0) return new LinkedList<>(Arrays.asList("[]"));
|
||||
var concat = new StringBuilder();
|
||||
for (var entry : stringified) {
|
||||
// We make a one-liner only when all members are one-liners
|
||||
if (entry.size() != 1) {
|
||||
concat = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (concat.length() != 0) concat.append(" ");
|
||||
concat.append(entry.get(0));
|
||||
}
|
||||
|
||||
// We don't want too long one-liners
|
||||
if (concat != null && concat.length() < 160) return new LinkedList<>(Arrays.asList("[" + concat.toString() + "]"));
|
||||
|
||||
var res = new LinkedList<String>();
|
||||
|
||||
res.add("[");
|
||||
|
||||
for (var entry : stringified) {
|
||||
for (var line : entry) {
|
||||
res.add(" " + line);
|
||||
}
|
||||
}
|
||||
res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1));
|
||||
res.add("]");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
var ignored = new HashSet<String>();
|
||||
var lines = toReadableBase(env, passed, ignored);
|
||||
|
||||
var superLines = new LinkedList<String>(super.toReadableLines(env, passed, ignored));
|
||||
if (superLines.size() == 1 && superLines.getFirst().equals("{}")) return lines;
|
||||
|
||||
lines.set(lines.size() - 1, lines.getLast() + " " + superLines.getFirst());
|
||||
lines.addAll(superLines.subList(1, superLines.size()));
|
||||
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package me.topchetoeu.jscript.runtime.values.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
@ -10,6 +14,7 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.PropertyMember;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
|
||||
@ -134,6 +139,81 @@ public class ObjectValue extends Value {
|
||||
return setPrototype(_env -> val);
|
||||
}
|
||||
|
||||
private final LinkedList<String> memberToReadable(Environment env, String key, Member member, HashSet<ObjectValue> passed) {
|
||||
if (member instanceof PropertyMember prop) {
|
||||
if (prop.getter == null && prop.setter == null) return new LinkedList<>(Arrays.asList(key + ": [No accessors]"));
|
||||
else if (prop.getter == null) return new LinkedList<>(Arrays.asList(key + ": [Setter]"));
|
||||
else if (prop.setter == null) return new LinkedList<>(Arrays.asList(key + ": [Getter]"));
|
||||
else return new LinkedList<>(Arrays.asList(key + ": [Getter/Setter]"));
|
||||
}
|
||||
else {
|
||||
var res = new LinkedList<String>();
|
||||
var first = true;
|
||||
|
||||
for (var line : member.get(env, this).toReadableLines(env, passed)) {
|
||||
if (first) res.add(key + ": " + line);
|
||||
else res.add(line);
|
||||
first = false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed, HashSet<String> ignoredKeys) {
|
||||
passed.add(this);
|
||||
|
||||
var stringified = new LinkedList<LinkedList<String>>();
|
||||
|
||||
for (var entry : getOwnSymbolMembers(env, true)) {
|
||||
var member = getOwnMember(env, entry);
|
||||
stringified.add(memberToReadable(env, "[" + entry.value + "]", member, passed));
|
||||
}
|
||||
for (var entry : getOwnMembers(env, true)) {
|
||||
if (ignoredKeys.contains(entry)) continue;
|
||||
|
||||
var member = getOwnMember(env, entry);
|
||||
stringified.add(memberToReadable(env, entry, member, passed));
|
||||
}
|
||||
|
||||
passed.remove(this);
|
||||
|
||||
if (stringified.size() == 0) return Arrays.asList("{}");
|
||||
var concat = new StringBuilder();
|
||||
for (var entry : stringified) {
|
||||
// We make a one-liner only when all members are one-liners
|
||||
if (entry.size() != 1) {
|
||||
concat = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (concat.length() != 0) concat.append(", ");
|
||||
concat.append(entry.get(0));
|
||||
}
|
||||
|
||||
// We don't want too long one-liners
|
||||
if (concat != null && concat.length() < 80) return Arrays.asList("{ " + concat.toString() + " }");
|
||||
|
||||
var res = new LinkedList<String>();
|
||||
|
||||
res.add("{");
|
||||
|
||||
for (var entry : stringified) {
|
||||
for (var line : entry) {
|
||||
res.add(" " + line);
|
||||
}
|
||||
|
||||
res.set(res.size() - 1, res.getLast() + ",");
|
||||
}
|
||||
res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1));
|
||||
res.add("}");
|
||||
|
||||
return res;
|
||||
}
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return toReadableLines(env, passed, new HashSet<>());
|
||||
}
|
||||
|
||||
public final boolean setPrototype(PrototypeProvider val) {
|
||||
if (!getState().extendable) return false;
|
||||
prototype = val;
|
||||
|
@ -1,10 +1,15 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.json.JSON;
|
||||
import me.topchetoeu.jscript.common.json.JSONElement;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
@ -67,6 +72,10 @@ public final class StringValue extends PrimitiveValue {
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(JSON.stringify(JSONElement.string(value)));
|
||||
}
|
||||
|
||||
private StringValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
@ -22,6 +26,10 @@ public final class VoidValue extends PrimitiveValue {
|
||||
throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env)));
|
||||
}
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(name);
|
||||
}
|
||||
|
||||
public VoidValue(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
|
@ -1,5 +1,12 @@
|
||||
package me.topchetoeu.jscript.runtime.values.primitives.numbers;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public final class IntValue extends NumberValue {
|
||||
public final long value;
|
||||
|
||||
@ -26,6 +33,10 @@ public final class IntValue extends NumberValue {
|
||||
else return false;
|
||||
}
|
||||
|
||||
@Override public List<String> toReadableLines(Environment env, HashSet<ObjectValue> passed) {
|
||||
return Arrays.asList(value + "i");
|
||||
}
|
||||
|
||||
public IntValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user