add JsonParser
This commit is contained in:
parent
7e44eda639
commit
1da002ce70
@ -1,6 +1,8 @@
|
|||||||
package eu.jonahbauer.json;
|
package eu.jonahbauer.json;
|
||||||
|
|
||||||
import eu.jonahbauer.json.exceptions.JsonConversionException;
|
import eu.jonahbauer.json.exceptions.JsonConversionException;
|
||||||
|
import eu.jonahbauer.json.exceptions.JsonParserException;
|
||||||
|
import eu.jonahbauer.json.parser.JsonParser;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -41,6 +43,17 @@ public sealed interface JsonValue extends Serializable permits JsonObject, JsonA
|
|||||||
return value == null ? "null" : value.toPrettyJsonString();
|
return value == null ? "null" : value.toPrettyJsonString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string as a JSON. The string must adhere to the <a href="https://www.json.org">JSON grammar</a>.
|
||||||
|
* @param json a string
|
||||||
|
* @return the result of parsing the string as a JSON
|
||||||
|
* @throws JsonParserException if the string cannot be parsed as JSON
|
||||||
|
*/
|
||||||
|
static @Nullable JsonValue parse(@NotNull String json) {
|
||||||
|
var parser = new JsonParser(json);
|
||||||
|
return parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an object to a JSON value:
|
* Converts an object to a JSON value:
|
||||||
* <ul>
|
* <ul>
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package eu.jonahbauer.json.exceptions;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class JsonParserException extends JsonReaderException {
|
||||||
|
|
||||||
|
public JsonParserException(int line, int column, @NotNull String message) {
|
||||||
|
super(line, column, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonParserException(int line, int column, @NotNull String message, @NotNull Throwable cause) {
|
||||||
|
super(line, column, message, cause);
|
||||||
|
}
|
||||||
|
}
|
242
core/src/main/java/eu/jonahbauer/json/parser/JsonParser.java
Normal file
242
core/src/main/java/eu/jonahbauer/json/parser/JsonParser.java
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
package eu.jonahbauer.json.parser;
|
||||||
|
|
||||||
|
import eu.jonahbauer.json.*;
|
||||||
|
import eu.jonahbauer.json.exceptions.JsonParserException;
|
||||||
|
import eu.jonahbauer.json.exceptions.JsonTokenizerException;
|
||||||
|
import eu.jonahbauer.json.tokenizer.JsonTokenizer;
|
||||||
|
import eu.jonahbauer.json.tokenizer.JsonTokenizerImpl;
|
||||||
|
import eu.jonahbauer.json.tokenizer.token.JsonNull;
|
||||||
|
import eu.jonahbauer.json.tokenizer.token.JsonPunctuation;
|
||||||
|
import eu.jonahbauer.json.tokenizer.token.JsonToken;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public final class JsonParser {
|
||||||
|
private final @NotNull JsonTokenizer tokenizer;
|
||||||
|
|
||||||
|
public JsonParser(@NotNull String json) {
|
||||||
|
this(new JsonTokenizerImpl(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonParser(@NotNull Reader reader) {
|
||||||
|
this(new JsonTokenizerImpl(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonParser(@NotNull JsonTokenizer tokenizer) {
|
||||||
|
this.tokenizer = Objects.requireNonNull(tokenizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the input as a JSON value.
|
||||||
|
* @return the JSON value
|
||||||
|
* @throws JsonParserException if the input could not be parsed as JSON
|
||||||
|
* @throws UncheckedIOException if an I/O error occurs
|
||||||
|
*/
|
||||||
|
public @Nullable JsonValue parse() throws JsonParserException {
|
||||||
|
try {
|
||||||
|
var out = parse0();
|
||||||
|
if (tokenizer.next() != null) {
|
||||||
|
throw new JsonParserException(tokenizer.getLineNumber(), tokenizer.getColumnNumber(), "unexpected trailing entries");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UncheckedIOException(ex);
|
||||||
|
} catch (JsonTokenizerException ex) {
|
||||||
|
throw new JsonParserException(ex.getLineNumber(), ex.getColumnNumber(), ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable JsonValue parse0() throws IOException {
|
||||||
|
// Use a stack of Context objects to store the objects and arrays which are currently being parsed.
|
||||||
|
// This avoids the risk of stack overflow which would exist when having one nested calls to parse0.
|
||||||
|
var stack = new ArrayDeque<@NotNull Context>();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
JsonValue value;
|
||||||
|
|
||||||
|
var token = tokenizer.next();
|
||||||
|
if (!stack.isEmpty() && token == stack.peek().end()) {
|
||||||
|
value = stack.pop().build();
|
||||||
|
} else {
|
||||||
|
// consume value separator
|
||||||
|
if (stack.peek() instanceof Context context && !context.isEmpty()) {
|
||||||
|
if (token != JsonPunctuation.VALUE_SEPARATOR) {
|
||||||
|
throw unexpectedToken(token, context.end(), JsonPunctuation.VALUE_SEPARATOR);
|
||||||
|
}
|
||||||
|
token = tokenizer.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// read object key and consume name separator
|
||||||
|
if (stack.peek() instanceof ObjectContext object) {
|
||||||
|
var key = parseJsonObjectKey(object, token);
|
||||||
|
if ((token = tokenizer.next()) != JsonPunctuation.NAME_SEPARATOR) {
|
||||||
|
throw unexpectedToken(token, JsonPunctuation.NAME_SEPARATOR);
|
||||||
|
}
|
||||||
|
token = tokenizer.next();
|
||||||
|
object.setKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == JsonPunctuation.BEGIN_OBJECT) {
|
||||||
|
stack.push(new ObjectContext());
|
||||||
|
continue;
|
||||||
|
} else if (token == JsonPunctuation.BEGIN_ARRAY) {
|
||||||
|
stack.push(new ArrayContext());
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// read json value
|
||||||
|
value = switch (token) {
|
||||||
|
case JsonPunctuation.BEGIN_ARRAY, JsonPunctuation.BEGIN_OBJECT -> throw new AssertionError();
|
||||||
|
case JsonNull.NULL -> null;
|
||||||
|
case JsonValue v -> v;
|
||||||
|
case JsonPunctuation _ -> throw unexpectedStartOfValue(token);
|
||||||
|
case null -> throw unexpectedEndOfFile();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack.peek() instanceof Context context) {
|
||||||
|
context.add(value);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull String parseJsonObjectKey(@NotNull ObjectContext context, @Nullable JsonToken token) {
|
||||||
|
return switch (token) {
|
||||||
|
case JsonString string -> string.value();
|
||||||
|
case null -> throw unexpectedEndOfFile();
|
||||||
|
default -> throw unexpectedStartOfKey(context, token);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull JsonParserException unexpectedStartOfKey(@NotNull ObjectContext context, @Nullable JsonToken token) {
|
||||||
|
if (context.isEmpty()) {
|
||||||
|
throw unexpectedToken(token, "JSON_STRING", JsonPunctuation.END_OBJECT);
|
||||||
|
} else {
|
||||||
|
// if the context is not empty, we have already consumed a VALUE_SEPARATOR(,) and standard JSON does not
|
||||||
|
// allow trailing commas
|
||||||
|
throw unexpectedToken(token, "JSON_STRING");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull JsonParserException unexpectedStartOfValue(@Nullable JsonToken token) {
|
||||||
|
var expected = new Object[] {
|
||||||
|
JsonPunctuation.BEGIN_OBJECT, JsonPunctuation.BEGIN_ARRAY, JsonBoolean.TRUE, JsonBoolean.FALSE,
|
||||||
|
JsonNull.NULL, "JSON_STRING", "JSON_NUMBER"
|
||||||
|
};
|
||||||
|
throw unexpectedToken(token, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull JsonParserException unexpectedEndOfFile() {
|
||||||
|
return new JsonParserException(tokenizer.getLineNumber(), tokenizer.getColumnNumber(), "unexpected end of file");
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull JsonParserException unexpectedToken(@Nullable JsonToken token, @NotNull Object @NotNull... expected) {
|
||||||
|
if (expected.length == 1) {
|
||||||
|
throw new JsonParserException(
|
||||||
|
tokenizer.getLineNumber(), tokenizer.getColumnNumber(),
|
||||||
|
"unexpected token: " + token + " (expected " + expected[0] + ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new JsonParserException(
|
||||||
|
tokenizer.getLineNumber(), tokenizer.getColumnNumber(),
|
||||||
|
"unexpected token: " + token + " (expected one of " + Arrays.toString(expected) + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an JSON object or array that is currently being parsed.
|
||||||
|
*/
|
||||||
|
private sealed interface Context {
|
||||||
|
boolean isEmpty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a nested JSON value to this context
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
void add(@Nullable JsonValue value);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and returns the JSON value represented by this context.
|
||||||
|
* @return a JSON value
|
||||||
|
*/
|
||||||
|
@NotNull JsonValue build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the token that indicates the end of this context}
|
||||||
|
*/
|
||||||
|
@NotNull JsonPunctuation end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a JSON object that is currently being parsed.
|
||||||
|
*/
|
||||||
|
private static final class ObjectContext implements Context {
|
||||||
|
private final @NotNull SequencedMap<@NotNull String, @Nullable JsonValue> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private @Nullable String key;
|
||||||
|
|
||||||
|
public void setKey(@Nullable String key) {
|
||||||
|
if (this.key != null) throw new IllegalStateException();
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(@Nullable JsonValue value) {
|
||||||
|
if (key == null) throw new IllegalStateException();
|
||||||
|
map.put(key, value);
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JsonValue build() {
|
||||||
|
if (key != null) throw new IllegalStateException();
|
||||||
|
return new JsonObject(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JsonPunctuation end() {
|
||||||
|
return JsonPunctuation.END_OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a JSON array that is currently being parsed.
|
||||||
|
*/
|
||||||
|
private static final class ArrayContext implements Context {
|
||||||
|
private final @NotNull List<@Nullable JsonValue> list = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(@Nullable JsonValue value) {
|
||||||
|
list.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return list.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JsonArray build() {
|
||||||
|
return new JsonArray(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JsonPunctuation end() {
|
||||||
|
return JsonPunctuation.END_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,4 +6,5 @@ module eu.jonahbauer.json {
|
|||||||
exports eu.jonahbauer.json.exceptions;
|
exports eu.jonahbauer.json.exceptions;
|
||||||
exports eu.jonahbauer.json.tokenizer;
|
exports eu.jonahbauer.json.tokenizer;
|
||||||
exports eu.jonahbauer.json.tokenizer.token;
|
exports eu.jonahbauer.json.tokenizer.token;
|
||||||
|
exports eu.jonahbauer.json.parser;
|
||||||
}
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
package eu.jonahbauer.json.parser;
|
||||||
|
|
||||||
|
import eu.jonahbauer.json.exceptions.JsonParserException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class JsonParserTest {
|
||||||
|
|
||||||
|
// https://github.com/nst/JSONTestSuite
|
||||||
|
@ParameterizedTest(name = "{0}")
|
||||||
|
@MethodSource("parameters")
|
||||||
|
void suite(@NotNull String name) throws IOException {
|
||||||
|
var path = "/nst/JsonTestSuite/" + name;
|
||||||
|
Boolean expected = switch (name.charAt(0)) {
|
||||||
|
case 'i' -> null;
|
||||||
|
case 'y' -> true;
|
||||||
|
case 'n' -> false;
|
||||||
|
default -> throw new IllegalArgumentException();
|
||||||
|
};
|
||||||
|
|
||||||
|
try (
|
||||||
|
var in = Objects.requireNonNull(JsonParserTest.class.getResourceAsStream(path));
|
||||||
|
var reader = new InputStreamReader(in)
|
||||||
|
) {
|
||||||
|
var parser = new JsonParser(reader);
|
||||||
|
|
||||||
|
if (expected == Boolean.FALSE) {
|
||||||
|
assertThrows(JsonParserException.class, parser::parse).printStackTrace(System.out);
|
||||||
|
} else if (expected == Boolean.TRUE) {
|
||||||
|
assertDoesNotThrow(parser::parse);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
parser.parse();
|
||||||
|
System.out.println("accepted");
|
||||||
|
} catch (JsonParserException ex) {
|
||||||
|
System.out.println("rejected");
|
||||||
|
ex.printStackTrace(System.out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull Stream<@NotNull Arguments> parameters() throws IOException {
|
||||||
|
var filenames = new ArrayList<Arguments>();
|
||||||
|
|
||||||
|
try (
|
||||||
|
InputStream in = Objects.requireNonNull(JsonParserTest.class.getResource("/nst/JsonTestSuite")).openStream();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(in))
|
||||||
|
) {
|
||||||
|
String resource;
|
||||||
|
while ((resource = br.readLine()) != null) {
|
||||||
|
filenames.add(Arguments.of(resource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filenames.stream();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
[123.456e-789]
|
@ -0,0 +1 @@
|
|||||||
|
[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
|
@ -0,0 +1 @@
|
|||||||
|
[-1e+9999]
|
@ -0,0 +1 @@
|
|||||||
|
[1.5e+9999]
|
@ -0,0 +1 @@
|
|||||||
|
[-123123e100000]
|
@ -0,0 +1 @@
|
|||||||
|
[123123e100000]
|
@ -0,0 +1 @@
|
|||||||
|
[123e-10000000]
|
@ -0,0 +1 @@
|
|||||||
|
[-123123123123123123123123123123]
|
@ -0,0 +1 @@
|
|||||||
|
[100000000000000000000]
|
@ -0,0 +1 @@
|
|||||||
|
[-237462374673276894279832749832423479823246327846]
|
@ -0,0 +1 @@
|
|||||||
|
{"\uDFAA":0}
|
@ -0,0 +1 @@
|
|||||||
|
["\uDADA"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uD888\u1234"]
|
@ -0,0 +1 @@
|
|||||||
|
["é"]
|
@ -0,0 +1 @@
|
|||||||
|
["日ш<E697A5>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22>"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uD800\n"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uDd1ea"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uD800\uD800\n"]
|
@ -0,0 +1 @@
|
|||||||
|
["\ud800"]
|
@ -0,0 +1 @@
|
|||||||
|
["\ud800abc"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22>"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uDd1e\uD834"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22>"]
|
@ -0,0 +1 @@
|
|||||||
|
["\uDFAA"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22><><EFBFBD><EFBFBD>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22><>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"]
|
@ -0,0 +1 @@
|
|||||||
|
["<22><>"]
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
|
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -0,0 +1 @@
|
|||||||
|
[1 true]
|
@ -0,0 +1 @@
|
|||||||
|
[a<EFBFBD>]
|
@ -0,0 +1 @@
|
|||||||
|
["": 1]
|
@ -0,0 +1 @@
|
|||||||
|
[""],
|
@ -0,0 +1 @@
|
|||||||
|
[,1]
|
@ -0,0 +1 @@
|
|||||||
|
[1,,2]
|
@ -0,0 +1 @@
|
|||||||
|
["x",,]
|
@ -0,0 +1 @@
|
|||||||
|
["x"]]
|
@ -0,0 +1 @@
|
|||||||
|
["",]
|
@ -0,0 +1 @@
|
|||||||
|
["x"
|
@ -0,0 +1 @@
|
|||||||
|
[x
|
@ -0,0 +1 @@
|
|||||||
|
[3[4]]
|
@ -0,0 +1 @@
|
|||||||
|
[<EFBFBD>]
|
@ -0,0 +1 @@
|
|||||||
|
[1:2]
|
@ -0,0 +1 @@
|
|||||||
|
[,]
|
@ -0,0 +1 @@
|
|||||||
|
[-]
|
@ -0,0 +1 @@
|
|||||||
|
[ , ""]
|
@ -0,0 +1,3 @@
|
|||||||
|
["a",
|
||||||
|
4
|
||||||
|
,1,
|
@ -0,0 +1 @@
|
|||||||
|
[1,]
|
@ -0,0 +1 @@
|
|||||||
|
[1,,]
|
@ -0,0 +1 @@
|
|||||||
|
["a"\f]
|
@ -0,0 +1 @@
|
|||||||
|
[*]
|
@ -0,0 +1 @@
|
|||||||
|
[""
|
@ -0,0 +1 @@
|
|||||||
|
[1,
|
@ -0,0 +1,3 @@
|
|||||||
|
[1,
|
||||||
|
1
|
||||||
|
,1
|
@ -0,0 +1 @@
|
|||||||
|
[{}
|
@ -0,0 +1 @@
|
|||||||
|
[fals]
|
@ -0,0 +1 @@
|
|||||||
|
[nul]
|
@ -0,0 +1 @@
|
|||||||
|
[tru]
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
[++1234]
|
@ -0,0 +1 @@
|
|||||||
|
[+1]
|
@ -0,0 +1 @@
|
|||||||
|
[+Inf]
|
@ -0,0 +1 @@
|
|||||||
|
[-01]
|
@ -0,0 +1 @@
|
|||||||
|
[-1.0.]
|
@ -0,0 +1 @@
|
|||||||
|
[-2.]
|
@ -0,0 +1 @@
|
|||||||
|
[-NaN]
|
@ -0,0 +1 @@
|
|||||||
|
[.-1]
|
@ -0,0 +1 @@
|
|||||||
|
[.2e-3]
|
@ -0,0 +1 @@
|
|||||||
|
[0.1.2]
|
@ -0,0 +1 @@
|
|||||||
|
[0.3e+]
|
@ -0,0 +1 @@
|
|||||||
|
[0.3e]
|
@ -0,0 +1 @@
|
|||||||
|
[0.e1]
|
@ -0,0 +1 @@
|
|||||||
|
[0E+]
|
@ -0,0 +1 @@
|
|||||||
|
[0E]
|
@ -0,0 +1 @@
|
|||||||
|
[0e+]
|
@ -0,0 +1 @@
|
|||||||
|
[0e]
|
@ -0,0 +1 @@
|
|||||||
|
[1.0e+]
|
@ -0,0 +1 @@
|
|||||||
|
[1.0e-]
|
@ -0,0 +1 @@
|
|||||||
|
[1.0e]
|
@ -0,0 +1 @@
|
|||||||
|
[1 000.0]
|
@ -0,0 +1 @@
|
|||||||
|
[1eE2]
|
@ -0,0 +1 @@
|
|||||||
|
[2.e+3]
|
@ -0,0 +1 @@
|
|||||||
|
[2.e-3]
|
@ -0,0 +1 @@
|
|||||||
|
[2.e3]
|
@ -0,0 +1 @@
|
|||||||
|
[9.e+]
|
@ -0,0 +1 @@
|
|||||||
|
[Inf]
|
@ -0,0 +1 @@
|
|||||||
|
[NaN]
|
@ -0,0 +1 @@
|
|||||||
|
[1]
|
@ -0,0 +1 @@
|
|||||||
|
[1+2]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user