1 /** 2 * Authors: Tomoya Tanjo 3 * Copyright: © 2021 Tomoya Tanjo 4 * License: Apache-2.0 5 */ 6 module salad.parser; 7 8 import dyaml : Node; 9 10 import salad.context : LoadingContext; 11 import salad.exception; 12 import salad.meta; 13 import salad.schema; 14 import salad.type; 15 16 /// 17 auto parse(alias module_)(Node node, string uri) 18 { 19 import std.traits : moduleName; 20 mixin("import "~moduleName!module_~";"); 21 import dyaml : NodeType; 22 23 alias T = DocumentRootType!module_; 24 alias ReturnType = SumType!(T, T[]); 25 26 if (node.type == NodeType.mapping) 27 { 28 import std.algorithm : map; 29 import std.array : array; 30 31 string baseuri; 32 if (auto base = "$base" in node) 33 { 34 baseuri = base.as!string; 35 } 36 else 37 { 38 baseuri = uri; 39 } 40 string[string] namespaces; 41 if (auto ns = "$namespaces" in node) 42 { 43 import std.array : assocArray; 44 import std.typecons : tuple; 45 namespaces = ns.mapping 46 .map!(a => tuple(a.key.as!string, a.value.as!string)) 47 .assocArray; 48 } 49 string[] schemas; 50 if (auto s = "$schemas" in node) 51 { 52 schemas = s.sequence.map!(a => a.as!string).array; 53 } 54 55 auto context = LoadingContext(baseuri, namespaces); 56 57 if (auto g = "$graph" in node) 58 { 59 import salad.resolver : preprocess; 60 61 return ReturnType(g.sequence.map!((a) { 62 import salad.util : edig; 63 a = a.preprocess(context); 64 T ret; 65 mixin(Assign_!("a", "ret", T)); 66 return ret; 67 }).array); 68 } 69 else 70 { 71 import salad.util : edig; 72 T ret; 73 mixin(Assign_!("node", "ret", T)); 74 return ReturnType(ret); 75 } 76 } 77 else 78 { 79 assert(false, "Not yet supported"); 80 } 81 } 82 83 /// 84 auto importFromURI(alias module_)(string uri, string fragment = "") 85 { 86 import salad.fetcher : fetchNode, fragment_ = fragment; 87 import std.format : format; 88 import std.range : empty; 89 90 auto frag = uri.fragment_; 91 auto baseuri = uri[0..$-frag.length]; 92 auto node = fetchNode(baseuri); 93 auto objs = parse!module_(node, baseuri); 94 if (frag.empty) 95 { 96 frag = fragment; 97 } 98 99 alias RetType = typeof(objs); 100 alias DocType = DocumentRootType!module_; 101 return objs.match!( 102 (DocType doc) { 103 docEnforce(frag.empty || doc.match!(d => d.identifier) == frag, 104 "Mismatched fragment", node); 105 return objs; 106 }, 107 (DocType[] docs) { 108 if (frag.empty) 109 { 110 return objs; 111 } 112 else 113 { 114 import std.algorithm : filter; 115 import std.array : array; 116 auto elems = docs.filter!(e => e.tryMatch!(d => d.identifier) == frag) 117 .array; 118 docEnforce(!elems.empty, format!"No objects for fragment `%s`"(frag), node); 119 docEnforce(elems.length == 1, format!"Duplicated objects for fragment `%s`"(frag), node); 120 return RetType(elems[0]); 121 } 122 } 123 ); 124 }