diff --git a/build.gradle b/build.gradle index c7e17fd..33a7719 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,7 @@ dependencies { exclude group: 'org.json', module: 'json' } compile 'org.json:json:20160212' + compile 'org.yaml:snakeyaml:1.17' testCompile 'junit:junit:4.12' testCompile 'org.openjdk.jmh:jmh-core:1.13' diff --git a/examples/formats/yaml.own b/examples/formats/yaml.own new file mode 100644 index 0000000..cd2c08d --- /dev/null +++ b/examples/formats/yaml.own @@ -0,0 +1,44 @@ +use "yaml" + +println "Yaml encode" +println yamlencode({ + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +}) + +println "\nYaml decode" +x = yamldecode(" + name: \"std\" + scope: \"both\" + desc: \"Contains common functions\" + desc_ru: \"Содержит вспомогательные функции общего назначения\" + constants: [] + functions: + - + name: \"arrayCombine\" + args: \"keys, values\" + desc: \"creates map by combining two arrays\" + desc_ru: \"создаёт объект на основе двух массивов\" + - + name: \"typeof\" + args: \"value\" + desc: \"returns the type of value\" + desc_ru: \"возвращает тип переданного значения\" + example: |- + print typeof(1) // 1 (NUMBER) + print typeof(\"text\") // 2 (STRING) + print typeof([]) // 3 (ARRAY) +") +println x.name + ", scope: " + x.scope +println x.desc +for func : x.functions { + println " - " + func.name + "(" + func.args + ")" + println " " + func.desc +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java new file mode 100644 index 0000000..97ddf2f --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml.java @@ -0,0 +1,17 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import com.annimon.ownlang.modules.Module; + +/** + * + * @author aNNiMON + */ +public final class yaml implements Module { + + @Override + public void init() { + Functions.set("yamlencode", new yaml_encode()); + Functions.set("yamldecode", new yaml_decode()); + } +} diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java new file mode 100644 index 0000000..fa3d3fc --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_decode.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import java.util.List; +import java.util.Map; +import org.yaml.snakeyaml.Yaml; + +public final class yaml_decode implements Function { + + @Override + public Value execute(Value... args) { + Arguments.check(1, args.length); + try { + final String yamlRaw = args[0].asString(); + final Object root = new Yaml().load(yamlRaw); + final Value process = process(root); + return process; + } catch (Exception ex) { + throw new RuntimeException("Error while parsing yaml", ex); + } + } + + private Value process(Object obj) { + if (obj instanceof Map) { + return process((Map) obj); + } + if (obj instanceof List) { + return process((List) obj); + } + if (obj instanceof String) { + return new StringValue((String) obj); + } + if (obj instanceof Number) { + return NumberValue.of(((Number) obj)); + } + if (obj instanceof Boolean) { + return NumberValue.fromBoolean((Boolean) obj); + } + // NULL or other + return NumberValue.ZERO; + } + + private MapValue process(Map map) { + final MapValue result = new MapValue(map.size()); + for (Map.Entry entry : map.entrySet()) { + final String key = entry.getKey().toString(); + final Value value = process(entry.getValue()); + result.set(new StringValue(key), value); + } + return result; + } + + private ArrayValue process(List list) { + final int length = list.size(); + final ArrayValue result = new ArrayValue(length); + for (int i = 0; i < length; i++) { + final Value value = process(list.get(i)); + result.set(i, value); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java new file mode 100644 index 0000000..9c00be4 --- /dev/null +++ b/src/main/java/com/annimon/ownlang/modules/yaml/yaml_encode.java @@ -0,0 +1,56 @@ +package com.annimon.ownlang.modules.yaml; + +import com.annimon.ownlang.lib.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.yaml.snakeyaml.Yaml; + +public final class yaml_encode implements Function { + + @Override + public Value execute(Value... args) { + Arguments.check(1, args.length); + try { + final Object root = process(args[0]); + final String yamlRaw = new Yaml().dump(root); + return new StringValue(yamlRaw); + } catch (Exception ex) { + throw new RuntimeException("Error while creating yaml", ex); + } + } + + private Object process(Value val) { + switch (val.type()) { + case Types.ARRAY: + return process((ArrayValue) val); + case Types.MAP: + return process((MapValue) val); + case Types.NUMBER: + return val.raw(); + case Types.STRING: + return val.asString(); + default: + return null; + } + } + + private Object process(MapValue map) { + final Map result = new HashMap<>(); + for (Map.Entry entry : map) { + final String key = entry.getKey().asString(); + final Object value = process(entry.getValue()); + result.put(key, value); + } + return result; + } + + private Object process(ArrayValue array) { + final List result = new ArrayList<>(); + for (Value value : array) { + result.add(process(value)); + } + return result; + } +} \ No newline at end of file diff --git a/src/test/resources/modules/yaml/yamldecode.own b/src/test/resources/modules/yaml/yamldecode.own new file mode 100644 index 0000000..1cc2565 --- /dev/null +++ b/src/test/resources/modules/yaml/yamldecode.own @@ -0,0 +1,33 @@ +use "std" +use "yaml" +use "ounit" + +x = yamldecode(" + name: \"std\" + scope: \"both\" + desc: \"Contains common functions\" + desc_ru: \" \" + constants: [] + functions: + - + name: \"arrayCombine\" + args: \"keys, values\" + desc: \"creates map by combining two arrays\" + desc_ru: \" \" + - + name: \"typeof\" + args: \"value\" + desc: \"returns the type of value\" + desc_ru: \" \" + example: |- + print typeof(1) // 1 (NUMBER) + print typeof(\"text\") // 2 (STRING) + print typeof([]) // 3 (ARRAY) +") + +assertEquals("std", x.name) +assertEquals("both", x.scope) +assertEquals(0, length(x.constants)) +assertEquals(2, length(x.functions)) +assertEquals("arrayCombine", x.functions[0].name) +assertEquals(" ", x.functions[1].desc_ru) \ No newline at end of file diff --git a/src/test/resources/modules/yaml/yamlencode.own b/src/test/resources/modules/yaml/yamlencode.own new file mode 100644 index 0000000..a54e33d --- /dev/null +++ b/src/test/resources/modules/yaml/yamlencode.own @@ -0,0 +1,22 @@ +use "std" +use "yaml" +use "ounit" + +yml = yamlencode({ + "name": "Yaml Example", + "version": 1, + "arrayData": [ + 1, 2, 3, 4 + ], + "objectData": { + "key": "value", + 10: "1000" + } +}) +obj = yamldecode(yml) + +assertEquals("Yaml Example", obj.name) +assertEquals(1, obj.version) +assertEquals(4, length(obj.arrayData)) +assertEquals("value", obj.objectData.key) +assertEquals("1000", obj.objectData["10"]) \ No newline at end of file