1 /**
2  * Authors: Tomoya Tanjo
3  * Copyright: © 2021 Tomoya Tanjo
4  * License: Apache-2.0
5  */
6 module salad.ast;
7 
8 version(none):
9 import dyaml : Mark, Node;
10 
11 import salad.type;
12 
13 import std.typecons : Tuple;
14 
15 alias ASTNodeType = Optional!(bool,
16                               long,
17                               real,
18                               string,
19                               AST[string],
20                               AST[],
21                               AST, 
22                               Node);
23 
24 ///
25 class AST
26 {
27     Mark mark;
28     string vocabulary;
29     ASTNodeType value;
30 
31     ///
32     this(T)(Node node, string vocab, T val) // validate T type
33     {
34         mark = node.startMark;
35         vocabulary = vocab;
36         value = ASTNodeType(val);
37     }
38 
39     override string toString() @trusted
40     {
41         import std.conv : to;
42         import std.format : format;
43         return format!"AST(%s, %s)"(vocabulary, value.match!(v => v.to!string));
44     }
45 }
46 
47 /// dig
48 auto dig(T)(AST ast, string key, T default_)
49 {
50     return dig(ast, [key], default_);
51 }
52 
53 /// ditto
54 AST dig(T)(AST ast, string[] keys, T default_)
55 {
56     import std.algorithm : canFind;
57     import std.range : empty;
58 
59     if (auto a = ast.value.match!((AST a) => a, others => null))
60     {
61         return a.dig(keys, default_);
62     }
63     else if (auto n = ast.value.match!((Node n) => &n, others => null))
64     {
65         import salad.util : dig;
66         auto digged = n.dig(keys, default_);
67         return new AST(digged, "Any", digged);
68     }
69     else if (keys.empty)
70     {
71         return ast;
72     }
73 
74     auto k = keys[0];
75 
76     auto rec = ast.value.tryMatch!((AST[string] rec) => rec);
77     if (auto a = k in rec)
78     {
79         return (*a).dig(keys[1..$], default_);
80     }
81     else
82     {
83         if (k.canFind("://"))
84         {
85             static if (is(T: void[]))
86             {
87                 auto n = Node((Node[]).init);
88             }
89             else
90             {
91                 auto n = Node(default_);
92             }
93             return new AST(Node.init, "Any", n);
94         }
95         else
96         {
97             static if (is(T : void[]))
98             {
99                 auto def = (AST[]).init;
100             }
101             else
102             {
103                 auto def = default_;
104             }
105             return new AST(Node.init, "Any", def);
106         }
107     }
108 }
109 
110 /// edig
111 auto edig(Ex = Exception)(AST ast, string key)
112 {
113     return edig!Ex(ast, [key]);
114 }
115 
116 /// ditto
117 AST edig(Ex = Exception)(AST ast, string[] keys)
118 {
119     import std.algorithm : canFind;
120     import std.range : empty;
121 
122     if (auto a = ast.value.match!((AST a) => a, others => null))
123     {
124         return a.edig!Ex(keys);
125     }
126     else if (auto n = ast.value.match!((return ref Node n) => &n, others => null))
127     {
128         import salad.util : edig;
129         auto digged = (*n).edig!Ex(keys);
130         return new AST(digged, "Any", digged);
131     }
132     else if (keys.empty)
133     {
134         return ast;
135     }
136 
137     auto k = keys[0];
138 
139     auto rec = ast.value.tryMatch!((AST[string] rec) => rec);
140     if (auto a = k in rec)
141     {
142         return (*a).edig!Ex(keys[1..$]);
143     }
144     else
145     {
146         import std.format : format;
147         throw new Ex(format!"No such field: %s"(k));
148     }
149 }