feat: implement hidden integers

This commit is contained in:
TopchetoEU 2024-09-14 19:38:30 +03:00
parent e11d182631
commit e9f889576c
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
18 changed files with 295 additions and 142 deletions

View File

@ -14,8 +14,8 @@ import me.topchetoeu.jscript.runtime.values.functions.FunctionValue;
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.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public class InstructionRunner {
private static Value execReturn(Environment env, Instruction instr, Frame frame) {
@ -136,7 +136,7 @@ public class InstructionRunner {
case PUSH_UNDEFINED: frame.push(Value.UNDEFINED); break;
case PUSH_NULL: frame.push(Value.NULL); break;
case PUSH_BOOL: frame.push(BoolValue.of(instr.get(0))); break;
case PUSH_NUMBER: frame.push(new NumberValue(instr.get(0))); break;
case PUSH_NUMBER: frame.push(NumberValue.of((double)instr.get(0))); break;
case PUSH_STRING: frame.push(new StringValue(instr.get(0))); break;
default:
}

View File

@ -12,15 +12,15 @@ import me.topchetoeu.jscript.runtime.values.Value;
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.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public class JSONConverter {
public static Value toJs(JSONElement val) {
if (val.isBoolean()) return BoolValue.of(val.bool());
if (val.isString()) return new StringValue(val.string());
if (val.isNumber()) return new NumberValue(val.number());
if (val.isNumber()) return NumberValue.of(val.number());
if (val.isList()) return ArrayValue.of(val.list().stream().map(JSONConverter::toJs).collect(Collectors.toList()));
if (val.isMap()) {
var res = new ObjectValue();
@ -43,7 +43,7 @@ public class JSONConverter {
public static JSONElement fromJs(Environment env, Value val, HashSet<Object> prev) {
if (val instanceof BoolValue) return JSONElement.bool(((BoolValue)val).value);
if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).value);
if (val instanceof NumberValue) return JSONElement.number(((NumberValue)val).getDouble());
if (val instanceof StringValue) return JSONElement.string(((StringValue)val).value);
if (val == Value.NULL) return JSONElement.NULL;
if (val instanceof VoidValue) return null;

View File

@ -22,10 +22,10 @@ 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.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public class SimpleRepl {
static Thread engineTask;
@ -94,8 +94,8 @@ public class SimpleRepl {
var res = new ObjectValue();
res.setPrototype(null, null);
res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env).value)));
res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env).value)));
res.defineOwnMember(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env))));
res.defineOwnMember(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env))));
res.defineOwnMember(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key()));
res.defineOwnMember(env, "getSymbolDescriptor", new NativeFunction(args -> new StringValue(((SymbolValue)args.get(0)).value)));
@ -107,14 +107,14 @@ public class SimpleRepl {
res.setPrototype(null, null);
res.defineOwnMember(env, "parseInt", new NativeFunction(args -> {
var radix = args.get(1).toInt(env);
var nradix = args.get(1).toNumber(env);
var radix = nradix.isInt() ? nradix.getInt() : 10;
if (radix != 10 && args.get(0) instanceof NumberValue) {
return new NumberValue(args.get(0).toNumber(env).value - args.get(0).toNumber(env).value % 1);
}
else {
return NumberValue.parseInt(args.get(0).toString(), radix, false);
if (radix != 10 && args.get(0) instanceof NumberValue num) {
if (num.isInt()) return num;
else return NumberValue.of(num.getDouble() - num.getDouble() % 1);
}
else return NumberValue.parseInt(args.get(0).toString(), radix, false);
}));
res.defineOwnMember(env, "parseFloat", new NativeFunction(args -> {
if (args.get(0) instanceof NumberValue) {
@ -123,8 +123,8 @@ public class SimpleRepl {
else return NumberValue.parseFloat(args.get(0).toString(), false);
}));
res.defineOwnMember(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN())));
res.defineOwnMember(env, "NaN", new NumberValue(Double.NaN));
res.defineOwnMember(env, "Infinity", new NumberValue(Double.POSITIVE_INFINITY));
res.defineOwnMember(env, "NaN", NumberValue.NAN);
res.defineOwnMember(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY));
return res;
}
@ -229,14 +229,14 @@ public class SimpleRepl {
var func = (FunctionValue)args.get(0);
var self = args.get(1);
var funcArgs = (ArrayValue)args.get(2);
var name = args.get(3).toString(env).value;
var name = args.get(3).toString(env);
return func.invoke(env, name, self, funcArgs.toArray());
}));
res.defineOwnMember(env, "construct", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
var funcArgs = (ArrayValue)args.get(1);
var name = args.get(2).toString(env).value;
var name = args.get(2).toString(env);
return func.construct(env, name, funcArgs.toArray());
}));
@ -252,7 +252,7 @@ public class SimpleRepl {
return new StringValue(JSON.stringify(JSONConverter.fromJs(env, args.get(0))));
}));
res.defineOwnMember(env, "parse", new NativeFunction(args -> {
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env).value));
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
}));
res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0);
@ -288,7 +288,7 @@ public class SimpleRepl {
int[] i = new int[1];
res.defineOwnMember(env, "setGlobalPrototype", new NativeFunction(args -> {
var type = args.get(0).toString(env).value;
var type = args.get(0).toString(env);
var obj = (ObjectValue)args.get(1);
switch (type) {
@ -330,7 +330,7 @@ public class SimpleRepl {
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "setIntrinsic", new NativeFunction(args -> {
var name = args.get(0).toString(env).value;
var name = args.get(0).toString(env);
var val = args.get(1);
Value.intrinsics(environment).put(name, val);
@ -338,7 +338,7 @@ public class SimpleRepl {
return Value.UNDEFINED;
}));
res.defineOwnMember(env, "compile", new NativeFunction(args -> {
return Compiler.compileFunc(env, new Filename("jscript", "func" + i[0]++ + ".js"), args.get(0).toString(env).value);
return Compiler.compileFunc(env, new Filename("jscript", "func" + i[0]++ + ".js"), args.get(0).toString(env));
}));
return res;

View File

@ -66,7 +66,7 @@ public class EngineException extends RuntimeException {
public String toString(Environment env) {
var ss = new StringBuilder();
try {
ss.append(value.toString(env).value).append('\n');
ss.append(value.toString(env)).append('\n');
}
catch (EngineException e) {
var name = value.getMember(env, "name");
@ -74,10 +74,10 @@ public class EngineException extends RuntimeException {
if (name.isPrimitive() && desc.isPrimitive()) {
if (name instanceof VoidValue) ss.append("Error: ");
else ss.append(name.toString(env).value + ": ");
else ss.append(name.toString(env) + ": ");
if (desc instanceof VoidValue) ss.append("An error occurred");
else ss.append(desc.toString(env).value);
else ss.append(desc.toString(env));
ss.append("\n");
}

View File

@ -1,28 +1,39 @@
package me.topchetoeu.jscript.runtime.values;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class KeyCache {
public final Value value;
private Integer intCache;
private boolean isInt;
private int intCache;
private Double doubleCache;
private Boolean booleanCache;
private String stringCache;
public String toString(Environment env) {
if (stringCache != null) return stringCache;
else return stringCache = value.toString(env).value;
else return stringCache = value.toString(env);
}
public double toNumber(Environment env) {
if (doubleCache != null) return doubleCache;
else return doubleCache = value.toNumber(env).value;
if (doubleCache == null) {
var res = value.toNumber(env);
isInt = res.isInt();
intCache = res.getInt();
doubleCache = res.getDouble();
}
return doubleCache;
}
public boolean isInt(Environment env) {
if (doubleCache == null) toNumber(env);
return isInt;
}
public int toInt(Environment env) {
if (intCache != null) return intCache;
else return intCache = (int)toNumber(env);
if (doubleCache == null) toNumber(env);
return intCache;
}
public boolean toBoolean() {
if (booleanCache != null) return booleanCache;
@ -45,13 +56,13 @@ public final class KeyCache {
this.booleanCache = !value.equals("");
}
public KeyCache(int value) {
this.value = new NumberValue(value);
this.value = NumberValue.of(value);
this.intCache = value;
this.doubleCache = (double)value;
this.booleanCache = value != 0;
}
public KeyCache(double value) {
this.value = new NumberValue(value);
this.value = NumberValue.of(value);
this.intCache = (int)value;
this.doubleCache = value;
this.booleanCache = value != 0;

View File

@ -26,10 +26,10 @@ 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.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.VoidValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public abstract class Value {
public static enum State {
@ -81,7 +81,7 @@ public abstract class Value {
public abstract boolean isPrimitive();
public final boolean isNaN() {
return this instanceof NumberValue && Double.isNaN(((NumberValue)this).value);
return this == NumberValue.NAN || this instanceof NumberValue num && Double.isNaN(num.getDouble());
}
public Value call(Environment env, boolean isNew, String name, Value self, Value ...args) {
@ -116,12 +116,9 @@ public abstract class Value {
public abstract Value toPrimitive(Environment env);
public abstract NumberValue toNumber(Environment env);
public abstract StringValue toString(Environment env);
public abstract String toString(Environment env);
public abstract boolean toBoolean();
public final int toInt(Environment env) { return (int)toNumber(env).value; }
public final long toLong(Environment env) { return (long)toNumber(env).value; }
public final boolean isInstanceOf(Environment env, Value proto) {
for (var val = getPrototype(env); val != null; val = getPrototype(env)) {
if (val.equals(proto)) return true;
@ -525,7 +522,7 @@ public abstract class Value {
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 return this.toString(env).value;
else return this.toString(env);
}
public final String toReadable(Environment ext) {
@ -560,7 +557,11 @@ public abstract class Value {
return aStr.value.compareTo(bStr.value) <= 0;
}
else {
return a.toNumber(env).value <= b.toNumber(env).value;
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isLong() && nb.isLong()) return na.getLong() <= nb.getLong();
else return na.getDouble() <= nb.getDouble();
}
}
public static final boolean greaterOrEqual(Environment env, Value a, Value b) {
@ -571,7 +572,11 @@ public abstract class Value {
return aStr.value.compareTo(bStr.value) >= 0;
}
else {
return a.toNumber(env).value >= b.toNumber(env).value;
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isLong() && nb.isLong()) return na.getLong() >= nb.getLong();
else return na.getDouble() >= nb.getDouble();
}
}
public static final boolean less(Environment env, Value a, Value b) {
@ -579,10 +584,14 @@ public abstract class Value {
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) >= 0;
return aStr.value.compareTo(bStr.value) < 0;
}
else {
return a.toNumber(env).value < b.toNumber(env).value;
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isLong() && nb.isLong()) return na.getLong() < nb.getLong();
else return na.getDouble() < nb.getDouble();
}
}
public static final boolean greater(Environment env, Value a, Value b) {
@ -590,10 +599,14 @@ public abstract class Value {
b = b.toPrimitive(env);
if (a instanceof StringValue aStr && b instanceof StringValue bStr) {
return aStr.value.compareTo(bStr.value) >= 0;
return aStr.value.compareTo(bStr.value) > 0;
}
else {
return a.toNumber(env).value > b.toNumber(env).value;
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isLong() && nb.isLong()) return na.getLong() > nb.getLong();
else return na.getDouble() > nb.getDouble();
}
}
@ -602,56 +615,96 @@ public abstract class Value {
b = b.toPrimitive(env);
if (a instanceof StringValue || b instanceof StringValue) {
return new StringValue(a.toString(env).value + b.toString(env).value);
return new StringValue(a.toString(env) + b.toString(env));
}
else {
return new NumberValue(a.toNumber(env).value + b.toNumber(env).value);
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() + nb.getInt());
else return NumberValue.of(na.getDouble() + nb.getDouble());
}
}
public static final NumberValue subtract(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value - b.toNumber(env).value);
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt());
else return NumberValue.of(na.getDouble() - nb.getDouble());
}
public static final NumberValue multiply(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value - b.toNumber(env).value);
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt());
else return NumberValue.of(na.getDouble() - nb.getDouble());
}
public static final NumberValue divide(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value / b.toNumber(env).value);
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) {
var ia = na.getInt();
var ib = nb.getInt();
if (ib == 0) {
if (ia == 0) return NumberValue.NAN;
else if (ia > 0) return NumberValue.of(Double.POSITIVE_INFINITY);
else return NumberValue.of(Double.NEGATIVE_INFINITY);
}
else if (ia % ib != 0) return NumberValue.of((double)ia / ib);
else return NumberValue.of(ia / ib);
}
else return NumberValue.of(na.getDouble() / nb.getDouble());
}
public static final NumberValue modulo(Environment env, Value a, Value b) {
return new NumberValue(a.toNumber(env).value % b.toNumber(env).value);
var na = a.toNumber(env);
var nb = b.toNumber(env);
if (na.isInt() && nb.isInt()) {
var ia = na.getInt();
var ib = nb.getInt();
if (ib == 0) return NumberValue.NAN;
else return NumberValue.of(ia % ib);
}
else return NumberValue.of(na.getDouble() % nb.getDouble());
}
public static final NumberValue negative(Environment env, Value a) {
return new NumberValue(-a.toNumber(env).value);
var na = a.toNumber(env);
if (na.isInt()) return NumberValue.of(-na.getInt());
else return NumberValue.of(-na.getDouble());
}
public static final NumberValue and(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) & b.toInt(env));
return NumberValue.of(a.toNumber(env).getInt() & b.toNumber(env).getInt());
}
public static final NumberValue or(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) | b.toInt(env));
return NumberValue.of(a.toNumber(env).getInt() | b.toNumber(env).getInt());
}
public static final NumberValue xor(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) ^ b.toInt(env));
return NumberValue.of(a.toNumber(env).getInt() ^ b.toNumber(env).getInt());
}
public static final NumberValue bitwiseNot(Environment env, Value a) {
return new NumberValue(~a.toInt(env));
return NumberValue.of(~a.toNumber(env).getInt());
}
public static final NumberValue shiftLeft(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) << b.toInt(env));
return NumberValue.of(a.toNumber(env).getInt() << b.toNumber(env).getInt());
}
public static final NumberValue shiftRight(Environment env, Value a, Value b) {
return new NumberValue(a.toInt(env) >> b.toInt(env));
return NumberValue.of(a.toNumber(env).getInt() >> b.toNumber(env).getInt());
}
public static final NumberValue unsignedShiftRight(Environment env, Value a, Value b) {
long _a = a.toInt(env);
long _b = b.toInt(env);
long _a = a.toNumber(env).getLong() & 0xFFFFFFFF;
long _b = b.toNumber(env).getLong() & 0xFFFFFFFF;
if (_a < 0) _a += 0x100000000l;
if (_b < 0) _b += 0x100000000l;
return new NumberValue(_a >>> _b);
return NumberValue.of(_a >>> _b);
}
public static final boolean looseEqual(Environment env, Value a, Value b) {

View File

@ -6,8 +6,8 @@ import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public abstract class FunctionValue extends ObjectValue {
private static final StringValue typeString = new StringValue("function");
@ -25,13 +25,13 @@ public abstract class FunctionValue extends ObjectValue {
return new StringValue(name);
}
@Override public boolean set(Environment env, Value val, Value self) {
name = val.toString(env).value;
name = val.toString(env);
return true;
}
};
private final FieldMember lengthField = new FieldMember(this, true, false, false) {
@Override public Value get(Environment env, Value self) {
return new NumberValue(length);
return NumberValue.of(length);
}
@Override public boolean set(Environment env, Value val, Value self) {
return false;

View File

@ -4,11 +4,12 @@ import java.util.LinkedHashSet;
import java.util.Set;
import me.topchetoeu.jscript.common.environment.Environment;
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.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
public abstract class ArrayLikeValue extends ObjectValue {
private static class IndexField extends FieldMember {
@ -30,10 +31,16 @@ public abstract class ArrayLikeValue extends ObjectValue {
private final FieldMember lengthField = new FieldMember(this, false, false, true) {
@Override public Value get(Environment env, Value self) {
return new NumberValue(size());
return NumberValue.of(size());
}
@Override public boolean set(Environment env, Value val, Value self) {
return setSize(val.toInt(env));
var num = val.toNumber(env);
if (!num.isInt()) throw EngineException.ofRange("Invalid array length");
var i = num.getInt();
if (i < 0) throw EngineException.ofRange("Invalid array length");
return setSize(i);
}
};

View File

@ -5,7 +5,7 @@ import java.util.Iterator;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
// TODO: Make methods generic
public class ByteBufferValue extends ArrayLikeValue implements Iterable<Value> {
@ -26,11 +26,11 @@ public class ByteBufferValue extends ArrayLikeValue implements Iterable<Value> {
@Override public Value get(int i) {
if (i < 0 || i >= values.length) return null;
return new NumberValue(values[i]);
return NumberValue.of(values[i]);
}
@Override public boolean set(Environment env, int i, Value val) {
if (i < 0 || i >= values.length) return false;
values[i] = (byte)val.toNumber(env).value;
values[i] = (byte)val.toNumber(env).getInt();
return true;
}
@Override public boolean has(int i) {

View File

@ -11,9 +11,9 @@ 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.functions.FunctionValue;
import me.topchetoeu.jscript.runtime.values.primitives.NumberValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public class ObjectValue extends Value {
public static interface PrototypeProvider {
@ -56,7 +56,7 @@ public class ObjectValue extends Value {
throw EngineException.ofType("Value couldn't be converted to a primitive.");
}
@Override public StringValue toString(Environment env) { return toPrimitive(env).toString(env); }
@Override public String toString(Environment env) { return toPrimitive(env).toString(env); }
@Override public boolean toBoolean() { return true; }
@Override public NumberValue toNumber(Environment env) { return toPrimitive(env).toNumber(env); }
@Override public StringValue type() { return typeString; }

View File

@ -2,6 +2,7 @@ package me.topchetoeu.jscript.runtime.values.primitives;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class BoolValue extends PrimitiveValue {
public static final BoolValue TRUE = new BoolValue(true);
@ -13,10 +14,8 @@ public final class BoolValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; }
@Override public boolean toBoolean() { return value; }
@Override public NumberValue toNumber(Environment ext) {
return value ? new NumberValue(1) : new NumberValue(0);
}
@Override public StringValue toString(Environment ext) { return new StringValue(value ? "true" : "false"); }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.of(value ? 1 : 0); }
@Override public String toString(Environment ext) { return value ? "true" : "false"; }
@Override public ObjectValue getPrototype(Environment env) {
return env.get(BOOL_PROTO);

View File

@ -1,54 +0,0 @@
package me.topchetoeu.jscript.runtime.values.primitives;
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.objects.ObjectValue;
public final class NumberValue extends PrimitiveValue {
public static final NumberValue NAN = new NumberValue(Double.NaN);
private static final StringValue typeString = new StringValue("number");
public final double value;
@Override public StringValue type() { return typeString; }
@Override public boolean toBoolean() { return value != 0; }
@Override public NumberValue toNumber(Environment ext) { return this; }
@Override public StringValue toString(Environment ext) { return new StringValue(toString()); }
@Override public String toString() { return JSON.stringify(JSONElement.number(value)); }
@Override public ObjectValue getPrototype(Environment env) {
return env.get(NUMBER_PROTO);
}
@Override public boolean equals(Object other) {
if (other instanceof NumberValue val) return value == val.value;
else return false;
}
public NumberValue(double value) {
this.value = value;
}
public static NumberValue parseInt(String str, int radix, boolean relaxed) {
if (radix < 2 || radix > 36) return new NumberValue(Double.NaN);
str = str.trim();
var res = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
if (res.isSuccess()) {
if (relaxed || res.n == str.length()) return new NumberValue(res.result);
}
return new NumberValue(Double.NaN);
}
public static NumberValue parseFloat(String str, boolean relaxed) {
str = str.trim();
var res = Parsing.parseFloat(new Source(str), 0, true);
if (res.isSuccess()) {
if (relaxed || res.n == str.length()) return new NumberValue(res.result);
}
return new NumberValue(Double.NaN);
}
}

View File

@ -10,6 +10,7 @@ import me.topchetoeu.jscript.runtime.values.KeyCache;
import me.topchetoeu.jscript.runtime.values.Member;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class StringValue extends PrimitiveValue {
public final String value;
@ -20,13 +21,13 @@ public final class StringValue extends PrimitiveValue {
@Override public boolean toBoolean() { return !value.equals(""); }
@Override public NumberValue toNumber(Environment ext) {
var val = value.trim();
if (val.equals("")) return new NumberValue(0);
if (val.equals("")) return NumberValue.of(0);
var res = Parsing.parseNumber(new Source(val), 0, true);
if (res.isSuccess() && res.n == val.length()) return new NumberValue(res.result);
else return new NumberValue(Double.NaN);
if (res.isSuccess() && res.n == val.length()) return NumberValue.of(res.result);
else return NumberValue.NAN;
}
@Override public StringValue toString(Environment ext) { return this; }
@Override public String toString(Environment ext) { return value; }
@Override public boolean equals(Object other) {
if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value);
@ -43,7 +44,7 @@ public final class StringValue extends PrimitiveValue {
return FieldMember.of(this, new StringValue(value.charAt(i) + ""), false, true, false);
}
else if (key.toString(env).equals("length")) {
return FieldMember.of(this, new NumberValue(value.length()), false, false, false);
return FieldMember.of(this, NumberValue.of(value.length()), false, false, false);
}
else return super.getOwnMember(env, key);
}

View File

@ -6,6 +6,7 @@ import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Value;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class SymbolValue extends PrimitiveValue {
private static final HashMap<String, SymbolValue> registry = new HashMap<>();
@ -20,7 +21,7 @@ public final class SymbolValue extends PrimitiveValue {
@Override public StringValue type() { return typeString; }
@Override public boolean toBoolean() { return false; }
@Override public StringValue toString(Environment env) {
@Override public String toString(Environment env) {
throw EngineException.ofType("Cannot convert a Symbol value to a string");
}
@Override public NumberValue toNumber(Environment env) {

View File

@ -5,17 +5,16 @@ 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.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue;
public final class VoidValue extends PrimitiveValue {
private final StringValue nameString;
public final String name;
public final StringValue typeString;
@Override public StringValue type() { return typeString; }
@Override public boolean toBoolean() { return false; }
@Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; }
@Override public StringValue toString(Environment ext) { return nameString; }
@Override public String toString(Environment ext) { return name; }
@Override public ObjectValue getPrototype(Environment env) { return null; }
@ -26,6 +25,5 @@ public final class VoidValue extends PrimitiveValue {
public VoidValue(String name, StringValue type) {
this.name = name;
this.typeString = type;
this.nameString = new StringValue(name);
}
}

View File

@ -0,0 +1,38 @@
package me.topchetoeu.jscript.runtime.values.primitives.numbers;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
public final class DoubleValue extends NumberValue {
public final double value;
@Override public boolean isInt() {
return (int)value == value;
}
@Override public boolean isLong() {
return (long)value == value;
}
@Override public int getInt() {
return (int)value;
}
@Override public long getLong() {
return (long)value;
}
@Override public double getDouble() {
return value;
}
@Override public String toString() { return JSON.stringify(JSONElement.number(value)); }
@Override public boolean equals(Object other) {
if (other instanceof NumberValue val) return value == val.getDouble();
else return false;
}
/**
* This constructs a double value directly. In almost all cases, you want to use NumberValue.of instead
*/
public DoubleValue(double value) {
this.value = value;
}
}

View File

@ -0,0 +1,34 @@
package me.topchetoeu.jscript.runtime.values.primitives.numbers;
public final class IntValue extends NumberValue {
public final long value;
@Override public boolean isInt() {
return (value & 0xFFFFFFFF00000000l) == 0;
}
@Override public boolean isLong() {
return true;
}
@Override public int getInt() {
return (int)value;
}
@Override public long getLong() {
return value;
}
@Override public double getDouble() {
return value;
}
@Override public String toString() { return value + ""; }
@Override public boolean equals(Object other) {
if (other instanceof NumberValue val) return val.isLong() && value == val.getLong();
else return false;
}
public IntValue(long value) {
this.value = value;
}
public IntValue(int value) {
this.value = value;
}
}

View File

@ -0,0 +1,65 @@
package me.topchetoeu.jscript.runtime.values.primitives.numbers;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
import me.topchetoeu.jscript.runtime.values.primitives.PrimitiveValue;
import me.topchetoeu.jscript.runtime.values.primitives.StringValue;
public abstract class NumberValue extends PrimitiveValue {
public static final NumberValue NAN = new DoubleValue(Double.NaN);
private static final StringValue typeString = new StringValue("number");
@Override public final StringValue type() { return typeString; }
public abstract double getDouble();
public abstract int getInt();
public abstract long getLong();
public abstract boolean isLong();
public abstract boolean isInt();
public abstract boolean equals(Object other);
public abstract String toString();
@Override public final boolean toBoolean() { return getDouble() != 0; }
@Override public final NumberValue toNumber(Environment ext) { return this; }
@Override public final String toString(Environment ext) { return toString(); }
@Override public final ObjectValue getPrototype(Environment env) {
return env.get(NUMBER_PROTO);
}
public static NumberValue parseInt(String str, int radix, boolean relaxed) {
if (radix < 2 || radix > 36) return NumberValue.NAN;
str = str.trim();
var res = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true);
if (res.isSuccess()) {
if (relaxed || res.n == str.length()) return of(res.result);
}
return NumberValue.NAN;
}
public static NumberValue parseFloat(String str, boolean relaxed) {
str = str.trim();
var res = Parsing.parseFloat(new Source(str), 0, true);
if (res.isSuccess()) {
if (relaxed || res.n == str.length()) return of(res.result);
}
return NumberValue.NAN;
}
public static NumberValue of(double value) {
if (Double.isNaN(value)) return NAN;
else if ((int)value == value) return new IntValue((int)value);
else return new DoubleValue(value);
}
public static NumberValue of(long value) {
return new IntValue(value);
}
public static NumberValue of(int value) {
return new IntValue(value);
}
}