temporary commit

This commit is contained in:
2023-10-03 15:02:19 +08:00
parent 67161e92aa
commit 1747d946ec
13 changed files with 751 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
wson.version = 2023.009.30
wson.version = 2023.10.01
lys.version = 2023.09.26
lys.version = 2023.10.01

View File

@@ -31,7 +31,7 @@ dependencyResolutionManagement {
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 {
include(it)

16
wcfg/build.gradle Normal file
View 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']
}

View 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
;

View File

@@ -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;
}
}

View 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) {
}
}

View 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();
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}
}
}

View 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"
}
};

View File

@@ -0,0 +1,4 @@
value1 := {
"key" : "value",
"myself" : value1
};

View 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
}
};