Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON5 support #159

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class ConfigurateDevPlugin : Plugin<Project> {
header = rootProject.file("LICENSE_HEADER")
include("**/*.java")
include("**/*.kt")
// TODO: this is probably not very efficient
exclude { it.file.absolutePath.contains("generated") }
newLine = false
}
}
Expand Down
3 changes: 3 additions & 0 deletions etc/checkstyle/suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
<suppress id="LineLengthComment" files="JacksonVisitor\.java"/> <!-- Commented out code -->
<!-- We don't need javadoc on examples, they already go alongside documentation -->
<suppress checks="MissingJavadocMethod" files="src[\\/]main[\\/]java[\\/]org[\\/]spongepowered[\\/]configurate[\\/]examples[\\/].*"/>

<!-- Ignore generated files -->
<suppress checks=".*" files="build[\\/]generated(-src)?.*" />
</suppressions>
17 changes: 17 additions & 0 deletions format/json5/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import org.spongepowered.configurate.build.core

plugins {
id("org.spongepowered.configurate-component")
antlr
}

configurations.compile {
exclude("org.antlr", "antlr4")
}

dependencies {
antlr("org.antlr:antlr4:4.8-1")
implementation("org.antlr:antlr4-runtime:4.8-1")

api(core())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
grammar Json5;
/**
* A grammar for the JSON5 document format.
*
* Based on the example grammar for JSON from The Definitive ANTLR 4 Reference ch6,
* and the JSON5 and ECMAScript 5.1 specifications.
*/

@header {
package org.spongepowered.configurate.json5;
}

// Value types //

document: object EOF;

value: object#Compound
| array#Compound
| Json5NumericLiteral#Literal
| Json5String#Literal
| BooleanLiteral#Literal
| NullLiteral#Literal
;

object: '{' member (',' member)* ','? '}'
| '{' '}'
;
member : memberName ':' value;
memberName: IdentifierName
| Json5String
;

array: '[' value (',' value)* ','? ']'
| '[' ']'
;

NullLiteral: 'null';
BooleanLiteral: 'true'
| 'false';

// ECMA Identifiers and Keywords //

IdentifierName: IdentifierStart IdentifierPart*;

Keyword: 'break' | 'do' | 'instanceof' | 'typeof'
| 'case' | 'else' | 'new' | 'var'
| 'catch' | 'finally' | 'return' | 'void'
| 'continue' | 'for' | 'switch' | 'while'
| 'debugger' | 'function' | 'this' | 'with'
| 'default' | 'if' | 'throw'
| 'delete' | 'in' | 'try'
;

FutureReservedWord: 'class' | 'enum' | 'extends' | 'super'
| 'const' | 'export' | 'import'
;


fragment IdentifierStart: UnicodeLetter | '$' | '_' | ('\\' UnicodeEscapeSequence);
fragment IdentifierPart: IdentifierStart
| UnicodeCombiningMark
| UnicodeDigit
| UnicodeConnectorPunctuation
;

fragment UnicodeLetter: [\p{L}\p{Nl}]; // Unicode Letter or letterlike numeric character
fragment UnicodeCombiningMark: [\p{Mn}\p{Mc}]; // Non-spacing mark or Combining spacing mark
fragment UnicodeDigit: [\p{Nd}]; // Decimal number
fragment UnicodeConnectorPunctuation: [\p{Pc}];


// Strings //

WS: [ \t\n\r\u000B\u000C\u2028\u2029\ufeff]+ -> skip;

Json5String: '"' Json5DoubleStringCharacter* '"'
| '\'' Json5SingleStringCharacter* '\'';

fragment Json5DoubleStringCharacter: ~('\\' | '"' | '\r' | '\n')
| '\\' EscapeSequence
| LineContinuation
| '\u2028'
| '\u2029';

fragment Json5SingleStringCharacter: ~('\\' | '\'' | '\r' | '\n')
| '\\' EscapeSequence
| LineContinuation
| '\u2028'
| '\u2029';

fragment EscapeSequence: '0' DecimalDigit
| HexEscapeSequence
| UnicodeEscapeSequence
| ~[\r\n];

fragment LineContinuation: '\\' NL;


fragment HexEscapeSequence: 'x' HexDigit HexDigit;
fragment UnicodeEscapeSequence: 'u' HexDigit HexDigit HexDigit HexDigit;

// Numbers //

Json5NumericLiteral: [+-]? (NumericLiteral
| 'Inifinity'
| 'NaN');

fragment NumericLiteral: DecimalLiteral
| HexIntegerLiteral;

DecimalLiteral: DecimalIntegerLiteral '.' DecimalDigit* ExponentPart?
| '.' DecimalDigit+ ExponentPart?
| DecimalIntegerLiteral ExponentPart?;

HexIntegerLiteral: '0x' HexDigit+;
fragment DecimalIntegerLiteral: '0'
| NonZeroDigit DecimalDigit*;
fragment HexDigit: [0-9a-fA-F];
fragment DecimalDigit: '0' | NonZeroDigit;
fragment NonZeroDigit: [1-9];
fragment ExponentPart: [eE] [+-]? DecimalDigit+;

// Comments and newlines //

fragment NL : '\r'? '\n';

LINE_COMMENT: '//' .*? NL -> channel(HIDDEN);
BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Configurate
* Copyright (C) zml and Configurate contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spongepowered.configurate.json5;

/**
* Helpers for interacting with parsed values.
*/
final class Helpers {

private Helpers() {}

public static String unescape(final String input) {
return input; // TODO: implement
}

public static String unquote(final String input) {
return input.substring(1, input.length() - 1);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Configurate
* Copyright (C) zml and Configurate contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spongepowered.configurate.json5;

import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.DefaultErrorStrategy;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
import org.spongepowered.configurate.RepresentationHint;
import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
import org.spongepowered.configurate.loader.CommentHandler;
import org.spongepowered.configurate.loader.CommentHandlers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Writer;

// TODO: Package-private generated files -- we may have to provide our own templates (sigh)
// TODO: see https://github.com/antlr/antlr4/blob/master/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg

/**
* A loader for the <a href="https://json5.org">JSON5</a> language.
*
* <p>We use our own implementation, and aim to be fully complaint with the
* spec. Within those bounds, any features will be considered.</p>
*/
public final class Json5ConfigurationLoader extends AbstractConfigurationLoader<CommentedConfigurationNode> {

public static final RepresentationHint<NumberLayout> NUMBER_FORMAT = RepresentationHint.of("number_format", NumberLayout.class,
NumberLayout.DECIMAL);

public static Json5ConfigurationLoader.Builder builder() {
return new Builder();
}

Json5ConfigurationLoader(final @NonNull Builder builder) {
super(builder, new CommentHandler[]{CommentHandlers.SLASH_BLOCK, CommentHandlers.DOUBLE_SLASH});
}

@Override
protected void loadInternal(final CommentedConfigurationNode node, final BufferedReader reader) throws IOException {
final CharStream stream = CharStreams.fromReader(reader);
final Json5Lexer lexer = new Json5Lexer(stream);
final CommonTokenStream tokens = new CommonTokenStream(lexer);
final Json5Parser parser = new Json5Parser(tokens);
final ToNodeListener listener = new ToNodeListener(tokens, node);

// ANTLR 4 Reference 13.7, improving speed
// TODO: Use actions instead to avoid generating parse tree.
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
parser.removeErrorListeners();
parser.setErrorHandler(new BailErrorStrategy());
try {
ParseTreeWalker.DEFAULT.walk(listener, parser.document());
// success with the simpler method
} catch (final ParseCancellationException ex) {
// Reset node
node.setValue(null);
node.setComment(null);
// Reset state
tokens.seek(0);
parser.reset();

parser.addErrorListener(ConsoleErrorListener.INSTANCE); // TODO: Capture + throw all errors
parser.setErrorHandler(new DefaultErrorStrategy());

// with full prediction
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
ParseTreeWalker.DEFAULT.walk(listener, parser.document());
}
for (Token token : tokens.getTokens()) {
System.out.println(token);
}
}

@Override
protected void saveInternal(final ConfigurationNode node, final Writer writer) throws IOException {
// TODO: writing
}

@Override
public CommentedConfigurationNode createNode(final ConfigurationOptions options) {
return CommentedConfigurationNode.root(options);
}

public static final class Builder extends AbstractConfigurationLoader.Builder<Builder> {

// TODO: provide options to customize output

@Override
public @NonNull Json5ConfigurationLoader build() {
return new Json5ConfigurationLoader(this);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Configurate
* Copyright (C) zml and Configurate contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spongepowered.configurate.json5;

/**
* A method of presenting numbers.
*/
public enum NumberLayout {

/**
* Base-10 number representation.
*/
DECIMAL,

/**
* Base-16 number representation.
*/
HEX;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Configurate
* Copyright (C) zml and Configurate contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spongepowered.configurate.json5;

/**
* The preferred style for quoted strings.
*/
public enum QuotingStyle {

PREFER_SINGLE,
PREFER_DOUBLE,
PREFER_UNQUOTED;

}
Loading