temporary commit
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
wson.version = 2023.009.30
|
wson.version = 2023.10.01
|
||||||
|
|
||||||
lys.version = 2023.09.26
|
lys.version = 2023.10.01
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ dependencyResolutionManagement {
|
|||||||
|
|
||||||
rootProject.name = 'wson'
|
rootProject.name = 'wson'
|
||||||
|
|
||||||
def includeDirs = ['antlr', 'benchmark', 'test-utils', 'wson-cli', 'wson-w3c-json']
|
def includeDirs = ['antlr', 'benchmark', 'test-utils', 'wson-cli', 'wson-w3c-json', 'wcfg']
|
||||||
|
|
||||||
includeDirs.each {
|
includeDirs.each {
|
||||||
include(it)
|
include(it)
|
||||||
|
16
wcfg/build.gradle
Normal file
16
wcfg/build.gradle
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
plugins {
|
||||||
|
id 'antlr'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation catalog.jwo
|
||||||
|
implementation rootProject
|
||||||
|
|
||||||
|
antlr catalog.antlr
|
||||||
|
antlr catalog.antlr.runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
generateGrammarSource {
|
||||||
|
maxHeapSize = "64m"
|
||||||
|
arguments += ['-package', 'net.woggioni.wson.wcfg']
|
||||||
|
}
|
101
wcfg/src/main/antlr/net/woggioni/wson/wcfg/WCFG.g4
Normal file
101
wcfg/src/main/antlr/net/woggioni/wson/wcfg/WCFG.g4
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
grammar WCFG;
|
||||||
|
|
||||||
|
wcfg
|
||||||
|
: assignment*
|
||||||
|
;
|
||||||
|
|
||||||
|
assignment
|
||||||
|
: IDENTIFIER ':=' (expression | value) ';'
|
||||||
|
;
|
||||||
|
|
||||||
|
expression
|
||||||
|
: value ('<<' value)+
|
||||||
|
;
|
||||||
|
|
||||||
|
obj
|
||||||
|
: '{' pair (',' pair)* '}'
|
||||||
|
| '{' '}'
|
||||||
|
;
|
||||||
|
|
||||||
|
pair
|
||||||
|
: STRING ':' (expression | value)
|
||||||
|
;
|
||||||
|
|
||||||
|
array
|
||||||
|
: '[' value (',' value)* ']'
|
||||||
|
| '[' ']'
|
||||||
|
;
|
||||||
|
|
||||||
|
value
|
||||||
|
: NULL
|
||||||
|
| BOOLEAN
|
||||||
|
| NUMBER
|
||||||
|
| STRING
|
||||||
|
| IDENTIFIER
|
||||||
|
| obj
|
||||||
|
| array
|
||||||
|
;
|
||||||
|
|
||||||
|
BOOLEAN
|
||||||
|
: 'true'
|
||||||
|
| 'false'
|
||||||
|
;
|
||||||
|
|
||||||
|
NULL
|
||||||
|
: 'null'
|
||||||
|
;
|
||||||
|
|
||||||
|
NUMBER
|
||||||
|
: '-'? INT ('.' [0-9] +)? EXP?
|
||||||
|
;
|
||||||
|
|
||||||
|
IDENTIFIER: Letter LetterOrDigit*;
|
||||||
|
|
||||||
|
STRING
|
||||||
|
: '"' (ESC | SAFECODEPOINT)* '"'
|
||||||
|
;
|
||||||
|
|
||||||
|
fragment ESC
|
||||||
|
: '\\' (["\\/bfnrt] | UNICODE)
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
fragment UNICODE
|
||||||
|
: 'u' HEX HEX HEX HEX
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
fragment HEX
|
||||||
|
: [0-9a-fA-F]
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
fragment SAFECODEPOINT
|
||||||
|
: ~ ["\\\u0000-\u001F]
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
fragment INT
|
||||||
|
: '0' | [1-9] [0-9]*
|
||||||
|
;
|
||||||
|
|
||||||
|
// no leading zeros
|
||||||
|
|
||||||
|
fragment EXP
|
||||||
|
: [Ee] [+\-]? INT
|
||||||
|
;
|
||||||
|
|
||||||
|
fragment LetterOrDigit
|
||||||
|
: Letter
|
||||||
|
| [0-9]
|
||||||
|
;
|
||||||
|
|
||||||
|
fragment Letter
|
||||||
|
: [a-zA-Z$_] // these are the "java letters" below 0x7F
|
||||||
|
| ~[\u0000-\u007F\uD800-\uDBFF] // covers all characters above 0x7F which are not a surrogate
|
||||||
|
| [\uD800-\uDBFF] [\uDC00-\uDFFF] // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF
|
||||||
|
;
|
||||||
|
|
||||||
|
WS
|
||||||
|
: [ \t\n\r] + -> skip
|
||||||
|
;
|
@@ -0,0 +1,95 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.woggioni.wson.traversal.ValueIdentity;
|
||||||
|
import net.woggioni.wson.value.ObjectValue;
|
||||||
|
import net.woggioni.wson.xface.Value;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static net.woggioni.jwo.JWO.dynamicCast;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CompositeObjectValue implements ObjectValue {
|
||||||
|
|
||||||
|
private final List<ObjectValue> elements;
|
||||||
|
|
||||||
|
private ObjectValue wrapped;
|
||||||
|
|
||||||
|
public CompositeObjectValue(List<ObjectValue> elements, Value.Configuration cfg) {
|
||||||
|
this.elements = elements;
|
||||||
|
wrapped = ObjectValue.newInstance(cfg);
|
||||||
|
List<ValueIdentity> identities = new ArrayList<>();
|
||||||
|
for (ObjectValue element : elements) {
|
||||||
|
CompositeObjectValue compositeObjectValue;
|
||||||
|
if ((compositeObjectValue = dynamicCast(element, CompositeObjectValue.class)) != null) {
|
||||||
|
boolean differenceFound = false;
|
||||||
|
for (int i = 0; i < compositeObjectValue.elements.size(); i++) {
|
||||||
|
ObjectValue objectValue = compositeObjectValue.elements.get(i);
|
||||||
|
if (!differenceFound && (i >= identities.size() || !Objects.equals(
|
||||||
|
identities.get(i),
|
||||||
|
new ValueIdentity(compositeObjectValue.elements.get(i))))) {
|
||||||
|
differenceFound = true;
|
||||||
|
}
|
||||||
|
if (differenceFound) {
|
||||||
|
merge(wrapped, objectValue);
|
||||||
|
identities.add(new ValueIdentity(objectValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
merge(wrapped, element);
|
||||||
|
identities.add(new ValueIdentity(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void merge(ObjectValue v1, ObjectValue v2) {
|
||||||
|
for (Map.Entry<String, Value> entry : v2) {
|
||||||
|
Value putResult = v1.getOrPut(entry.getKey(), entry.getValue());
|
||||||
|
if (putResult != entry.getValue()) {
|
||||||
|
if (putResult.type() == Value.Type.OBJECT && entry.getValue().type() == Value.Type.OBJECT) {
|
||||||
|
ObjectValue ov = ObjectValue.newInstance();
|
||||||
|
merge(ov, (ObjectValue) putResult);
|
||||||
|
merge(ov, (ObjectValue) entry.getValue());
|
||||||
|
v1.put(entry.getKey(), ov);
|
||||||
|
} else {
|
||||||
|
v1.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Map.Entry<String, Value>> iterator() {
|
||||||
|
return wrapped.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value get(String key) {
|
||||||
|
return wrapped.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getOrDefault(String key, Value defaultValue) {
|
||||||
|
return wrapped.getOrDefault(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String key) {
|
||||||
|
return wrapped.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return wrapped.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Map.Entry<String, Value>> asObject() {
|
||||||
|
return this::iterator;
|
||||||
|
}
|
||||||
|
}
|
226
wcfg/src/main/java/net/woggioni/wson/wcfg/ListenerImpl.java
Normal file
226
wcfg/src/main/java/net/woggioni/wson/wcfg/ListenerImpl.java
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.woggioni.wson.value.ArrayValue;
|
||||||
|
import net.woggioni.wson.value.BooleanValue;
|
||||||
|
import net.woggioni.wson.value.FloatValue;
|
||||||
|
import net.woggioni.wson.value.IntegerValue;
|
||||||
|
import net.woggioni.wson.value.NullValue;
|
||||||
|
import net.woggioni.wson.value.ObjectValue;
|
||||||
|
import net.woggioni.wson.value.StringValue;
|
||||||
|
import net.woggioni.wson.xface.Value;
|
||||||
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
|
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||||
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.woggioni.jwo.JWO.dynamicCast;
|
||||||
|
|
||||||
|
class ListenerImpl implements WCFGListener {
|
||||||
|
|
||||||
|
private final Value.Configuration cfg;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Value result;
|
||||||
|
|
||||||
|
private interface StackLevel {
|
||||||
|
Value getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ArrayStackLevel implements StackLevel {
|
||||||
|
private final ArrayValue value = new ArrayValue();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ObjectStackLevel implements StackLevel {
|
||||||
|
public String currentKey;
|
||||||
|
private final ObjectValue value;
|
||||||
|
|
||||||
|
public ObjectStackLevel(Value.Configuration cfg) {
|
||||||
|
value = ObjectValue.newInstance(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExpressionStackLevel implements StackLevel {
|
||||||
|
private final Value.Configuration cfg;
|
||||||
|
private ObjectValue value = null;
|
||||||
|
public List<ObjectValue> elements = new ArrayList<>();
|
||||||
|
|
||||||
|
public ExpressionStackLevel(Value.Configuration cfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getValue() {
|
||||||
|
if(value == null) {
|
||||||
|
value = new CompositeObjectValue(elements, cfg);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<StackLevel> stack = new ArrayList<>();
|
||||||
|
|
||||||
|
private void add2Last(Value value) {
|
||||||
|
StackLevel last = stack.get(stack.size() - 1);
|
||||||
|
ArrayStackLevel asl;
|
||||||
|
ObjectStackLevel osl;
|
||||||
|
ExpressionStackLevel esl;
|
||||||
|
if ((asl = dynamicCast(last, ArrayStackLevel.class)) != null) {
|
||||||
|
asl.value.add(value);
|
||||||
|
} else if ((osl = dynamicCast(last, ObjectStackLevel.class)) != null) {
|
||||||
|
osl.value.put(osl.currentKey, value);
|
||||||
|
osl.currentKey = null;
|
||||||
|
} else if((esl = dynamicCast(last, ExpressionStackLevel.class)) != null) {
|
||||||
|
esl.elements.add((ObjectValue) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static String unquote(String quoted) {
|
||||||
|
return quoted.substring(1, quoted.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListenerImpl(Value.Configuration cfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
StackLevel sl = new ObjectStackLevel(cfg);
|
||||||
|
result = sl.getValue();
|
||||||
|
stack.add(sl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StackLevel pop() {
|
||||||
|
int size = stack.size() - 1;
|
||||||
|
StackLevel sl = stack.get(size);
|
||||||
|
stack.remove(size);
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterWcfg(WCFGParser.WcfgContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitWcfg(WCFGParser.WcfgContext ctx) {
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterAssignment(WCFGParser.AssignmentContext ctx) {
|
||||||
|
ObjectStackLevel osl = (ObjectStackLevel) stack.get(0);
|
||||||
|
osl.currentKey = ctx.IDENTIFIER().getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitAssignment(WCFGParser.AssignmentContext ctx) {
|
||||||
|
ObjectStackLevel osl = (ObjectStackLevel) stack.get(0);
|
||||||
|
osl.currentKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterExpression(WCFGParser.ExpressionContext ctx) {
|
||||||
|
ExpressionStackLevel esl = new ExpressionStackLevel(cfg);
|
||||||
|
stack.add(esl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitExpression(WCFGParser.ExpressionContext ctx) {
|
||||||
|
add2Last(pop().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterObj(WCFGParser.ObjContext ctx) {
|
||||||
|
ObjectStackLevel osl = new ObjectStackLevel(cfg);
|
||||||
|
stack.add(osl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitObj(WCFGParser.ObjContext ctx) {
|
||||||
|
add2Last(pop().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterPair(WCFGParser.PairContext ctx) {
|
||||||
|
ObjectStackLevel osl = (ObjectStackLevel) stack.get(stack.size() - 1);
|
||||||
|
osl.currentKey = unquote(ctx.STRING().getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitPair(WCFGParser.PairContext ctx) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterArray(WCFGParser.ArrayContext ctx) {
|
||||||
|
ArrayStackLevel asl = new ArrayStackLevel();
|
||||||
|
stack.add(asl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitArray(WCFGParser.ArrayContext ctx) {
|
||||||
|
add2Last(pop().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterValue(WCFGParser.ValueContext ctx) {
|
||||||
|
if (ctx.obj() != null) {
|
||||||
|
} else if (ctx.array() != null) {
|
||||||
|
} else if (ctx.STRING() != null) {
|
||||||
|
add2Last(new StringValue(unquote(ctx.STRING().getText())));
|
||||||
|
} else if (ctx.BOOLEAN() != null) {
|
||||||
|
add2Last(new BooleanValue(Boolean.parseBoolean(ctx.BOOLEAN().getText())));
|
||||||
|
} else if (ctx.NULL() != null) {
|
||||||
|
add2Last(new NullValue());
|
||||||
|
} else if (ctx.NUMBER() != null) {
|
||||||
|
String text = ctx.NUMBER().getText();
|
||||||
|
if (text.indexOf('.') < 0) {
|
||||||
|
add2Last(new IntegerValue(Long.parseLong(text)));
|
||||||
|
} else {
|
||||||
|
add2Last(new FloatValue(Double.parseDouble(text)));
|
||||||
|
}
|
||||||
|
} else if(ctx.IDENTIFIER() != null) {
|
||||||
|
String name = ctx.IDENTIFIER().getText();
|
||||||
|
Value referredValue = result.getOrDefault(name, null);
|
||||||
|
if(referredValue == null) {
|
||||||
|
throw new ParseError(
|
||||||
|
"Undeclared identifier '" + name + "'",
|
||||||
|
ctx.start.getLine(),
|
||||||
|
ctx.start.getCharPositionInLine() + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
add2Last(referredValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitValue(WCFGParser.ValueContext ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTerminal(TerminalNode node) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitErrorNode(ErrorNode node) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterEveryRule(ParserRuleContext ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exitEveryRule(ParserRuleContext ctx) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
15
wcfg/src/main/java/net/woggioni/wson/wcfg/ParseError.java
Normal file
15
wcfg/src/main/java/net/woggioni/wson/wcfg/ParseError.java
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class ParseError extends RuntimeException {
|
||||||
|
public ParseError(String message, int line, int column) {
|
||||||
|
super(message + String.format(" at %d:%d", line, column));
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return super.getMessage();
|
||||||
|
}
|
||||||
|
}
|
110
wcfg/src/main/java/net/woggioni/wson/wcfg/ValueHolder.java
Normal file
110
wcfg/src/main/java/net/woggioni/wson/wcfg/ValueHolder.java
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import net.woggioni.wson.xface.Value;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ValueHolder implements Value {
|
||||||
|
@Setter
|
||||||
|
private Value delegate = Value.Null;
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return delegate.type();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNull() {
|
||||||
|
return delegate.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean asBoolean() {
|
||||||
|
return delegate.asBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long asInteger() {
|
||||||
|
return delegate.asInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double asFloat() {
|
||||||
|
return delegate.asFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return delegate.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Value> asArray() {
|
||||||
|
return delegate.asArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Map.Entry<String, Value>> asObject() {
|
||||||
|
return delegate.asObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return delegate.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Value value) {
|
||||||
|
delegate.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int index, Value value) {
|
||||||
|
delegate.set(index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value pop() {
|
||||||
|
return delegate.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value head() {
|
||||||
|
return delegate.head();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value tail() {
|
||||||
|
return delegate.tail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value get(int index) {
|
||||||
|
return delegate.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String key, Value value) {
|
||||||
|
delegate.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value get(String key) {
|
||||||
|
return delegate.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getOrDefault(String key, Value defaultValue) {
|
||||||
|
return delegate.getOrDefault(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value getOrPut(String key, Value value2Put) {
|
||||||
|
return delegate.getOrPut(key, value2Put);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean has(String key) {
|
||||||
|
return delegate.has(key);
|
||||||
|
}
|
||||||
|
}
|
47
wcfg/src/main/java/net/woggioni/wson/wcfg/WConfig.java
Normal file
47
wcfg/src/main/java/net/woggioni/wson/wcfg/WConfig.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import net.woggioni.wson.value.ObjectValue;
|
||||||
|
import net.woggioni.wson.xface.Value;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CodePointCharStream;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||||
|
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class WConfig {
|
||||||
|
|
||||||
|
private final Value.Configuration cfg;
|
||||||
|
private final Value value;
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public WConfig(Reader reader, Value.Configuration cfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
CodePointCharStream inputStream = CharStreams.fromReader(reader);
|
||||||
|
WCFGLexer lexer = new WCFGLexer(inputStream);
|
||||||
|
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
|
||||||
|
WCFGParser parser = new WCFGParser(commonTokenStream);
|
||||||
|
ListenerImpl listener = new ListenerImpl(cfg);
|
||||||
|
ParseTreeWalker walker = new ParseTreeWalker();
|
||||||
|
walker.walk(listener, parser.wcfg());
|
||||||
|
value = listener.getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value get(String key) {
|
||||||
|
return value.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value get(String ...overrides) {
|
||||||
|
return new CompositeObjectValue(
|
||||||
|
Arrays.stream(overrides)
|
||||||
|
.map(k -> (ObjectValue) value.get(k))
|
||||||
|
.collect(Collectors.toList()), cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value whole() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
56
wcfg/src/test/java/net/woggioni/wson/wcfg/ParseTest.java
Normal file
56
wcfg/src/test/java/net/woggioni/wson/wcfg/ParseTest.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package net.woggioni.wson.wcfg;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import net.woggioni.wson.serialization.json.JSONDumper;
|
||||||
|
import net.woggioni.wson.xface.Value;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CodePointCharStream;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
|
||||||
|
public class ParseTest {
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
// "build.wcfg",
|
||||||
|
// "test.wcfg",
|
||||||
|
"recursive.wcfg",
|
||||||
|
})
|
||||||
|
public void test(String resource) {
|
||||||
|
try(Reader reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resource))) {
|
||||||
|
CodePointCharStream inputStream = CharStreams.fromReader(reader);
|
||||||
|
WCFGLexer lexer = new WCFGLexer(inputStream);
|
||||||
|
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
|
||||||
|
WCFGParser parser = new WCFGParser(commonTokenStream);
|
||||||
|
Value.Configuration cfg = Value.Configuration.builder().serializeReferences(true).build();
|
||||||
|
ListenerImpl listener = new ListenerImpl(cfg);
|
||||||
|
ParseTreeWalker walker = new ParseTreeWalker();
|
||||||
|
walker.walk(listener, parser.wcfg());
|
||||||
|
Value result = listener.getResult();
|
||||||
|
new JSONDumper(cfg).dump(result, System.out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
public void test2() {
|
||||||
|
Value.Configuration cfg = Value.Configuration.builder().serializeReferences(true).build();
|
||||||
|
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/build.wcfg"))) {
|
||||||
|
WConfig wConfig = new WConfig(reader, cfg);
|
||||||
|
Value result = wConfig.get("release", "dev");
|
||||||
|
try (OutputStream os = new BufferedOutputStream(new FileOutputStream("/tmp/build.json"))) {
|
||||||
|
new JSONDumper(cfg).dump(result, os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
wcfg/src/test/resources/build.wcfg
Normal file
55
wcfg/src/test/resources/build.wcfg
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
default := {
|
||||||
|
"source-directory": ".",
|
||||||
|
"generator": "Unix Makefiles",
|
||||||
|
"options": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dev := default << {
|
||||||
|
"options": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jenkins := {
|
||||||
|
"build-directory" : "build"
|
||||||
|
};
|
||||||
|
|
||||||
|
release := default << {
|
||||||
|
"clean": true,
|
||||||
|
"options": {
|
||||||
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
lin64 := default << {
|
||||||
|
"options" : {
|
||||||
|
"STATIC_LIBSTDCXX" : true,
|
||||||
|
"QT5_STATIC": "ON"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win64 := default << {
|
||||||
|
"options": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/x86_64-w64-mingw32/cmake/toolchain-x86_64-w64-mingw32.cmake",
|
||||||
|
"STATIC_BUILD": "ON",
|
||||||
|
"QT5_STATIC": "ON"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win32 := default << {
|
||||||
|
"options": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/i686-w64-mingw32/cmake/toolchain-i686-w64-mingw32.cmake",
|
||||||
|
"STATIC_BUILD": "ON",
|
||||||
|
"QT5_STATIC": "ON"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
musl := default << {
|
||||||
|
"options": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "/opt/x-tools/x86_64-woggioni-linux-musl/cmake/toolchain-x86_64-woggioni-linux-musl.cmake",
|
||||||
|
"QT5_STATIC": "ON",
|
||||||
|
"STATIC_BUILD": "ON"
|
||||||
|
}
|
||||||
|
};
|
4
wcfg/src/test/resources/recursive.wcfg
Normal file
4
wcfg/src/test/resources/recursive.wcfg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
value1 := {
|
||||||
|
"key" : "value",
|
||||||
|
"myself" : value1
|
||||||
|
};
|
23
wcfg/src/test/resources/test.wcfg
Normal file
23
wcfg/src/test/resources/test.wcfg
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
default := {
|
||||||
|
"foo" : 2,
|
||||||
|
"bar" : true
|
||||||
|
};
|
||||||
|
|
||||||
|
overridden := default << {
|
||||||
|
"foo" : 0,
|
||||||
|
"enabled" : false,
|
||||||
|
"window" : null
|
||||||
|
};
|
||||||
|
|
||||||
|
window := {
|
||||||
|
"width" : 640,
|
||||||
|
"height" : 480
|
||||||
|
};
|
||||||
|
|
||||||
|
foobar := {
|
||||||
|
"child" : overridden << {
|
||||||
|
"window" : window
|
||||||
|
} << {
|
||||||
|
"enabled" : true
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user