1 module dsemver.ast; 2 3 import std.stdio; 4 import std.array : array, appender; 5 import std.algorithm.iteration : map; 6 import std.algorithm.searching : endsWith, startsWith; 7 import std.json; 8 import std.typecons : Nullable, nullable; 9 import std.traits : isArray, isSomeString, isIntegral, isFloatingPoint, 10 FieldNameTuple, isBasicType; 11 import std.algorithm : sort, setDifference; 12 import std.range : ElementEncodingType; 13 import std.format; 14 import std.exception : enforce; 15 16 struct Parameter { 17 string name; 18 Nullable!(string) type; 19 Nullable!(string) deco; 20 Nullable!(string) kind; 21 Nullable!(string) defaultValue; 22 Nullable!(string) default_; 23 Nullable!(string[]) storageClass; 24 } 25 26 struct Member { 27 string name; 28 string kind; 29 Nullable!(string) originalType; 30 Nullable!(string) type; 31 Nullable!(string) base; 32 Nullable!(string) init_; 33 Nullable!(string) value; 34 Nullable!(string[]) storageClass; 35 Nullable!(string) deco; 36 Nullable!(string) baseDeco; 37 Nullable!(long) align_; 38 Nullable!(long) offset; 39 Nullable!(Parameter[]) parameters; 40 Nullable!(string[]) overrides; 41 Nullable!(string) protection; 42 Nullable!(string[]) selective; 43 Nullable!(Member[]) members; 44 } 45 46 struct Module { 47 Nullable!string name; 48 string kind; 49 string file; 50 Member[] members; 51 } 52 53 string moduleToName(ref const(Module) mod) pure @safe { 54 return mod.name.isNull 55 ? mod.file 56 : mod.name.get(); 57 } 58 59 string toString(ref const(Parameter) p) { 60 auto app = appender!string(); 61 formattedWrite(app, "\"%s", p.name); 62 if(!p.type.isNull()) { 63 formattedWrite(app, " type %s", p.type.get()); 64 } 65 if(!p.deco.isNull()) { 66 formattedWrite(app, " deco %s", p.deco.get()); 67 } 68 if(!p.kind.isNull()) { 69 formattedWrite(app, " kind %s", p.kind.get()); 70 } 71 if(!p.defaultValue.isNull()) { 72 formattedWrite(app, " defaultValue %s", p.defaultValue.get()); 73 } 74 if(!p.default_.isNull()) { 75 formattedWrite(app, " default %s", p.default_.get()); 76 } 77 if(!p.storageClass.isNull()) { 78 formattedWrite(app, " storageClass %(%s, %)", p.storageClass.get()); 79 } 80 app.put("\""); 81 return app.data; 82 } 83 84 string toString(ref const(Member) mem) { 85 import core.demangle; 86 auto app = appender!string(); 87 formattedWrite(app, "\"%s", mem.kind); 88 formattedWrite(app, " '%s'", mem.name); 89 if(!mem.originalType.isNull) { 90 formattedWrite(app, " original_type '%s'", mem.originalType.get()); 91 } 92 if(!mem.type.isNull) { 93 formattedWrite(app, " type '%s'", mem.type.get()); 94 } 95 if(!mem.value.isNull) { 96 formattedWrite(app, " value '%s'", mem.value.get()); 97 } 98 if(!mem.storageClass.isNull) { 99 formattedWrite(app, " storage class '%s'", mem.storageClass.get()); 100 } 101 if(!mem.deco.isNull) { 102 formattedWrite(app, " of type '%s'", demangleType(mem.deco.get())); 103 } 104 if(!mem.baseDeco.isNull) { 105 formattedWrite(app, " '%s'", mem.baseDeco.get()); 106 } 107 if(!mem.align_.isNull) { 108 formattedWrite(app, " align '%s'", mem.align_.get()); 109 } 110 if(!mem.base.isNull) { 111 formattedWrite(app, " base '%s'", mem.base.get()); 112 } 113 if(!mem.offset.isNull) { 114 formattedWrite(app, " offset '%s'", mem.offset.get()); 115 } 116 if(!mem.init_.isNull) { 117 formattedWrite(app, " init '%s'", mem.init_.get()); 118 } 119 if(!mem.parameters.isNull) { 120 formattedWrite(app, " parameters '%(%s, %)'" 121 , mem.parameters.get().map!(p => p.toString())); 122 } 123 if(!mem.overrides.isNull) { 124 formattedWrite(app, " overrides '%(%s, %)'", mem.overrides.get()); 125 } 126 if(!mem.protection.isNull) { 127 formattedWrite(app, " protection '%s'", mem.protection.get()); 128 } 129 if(!mem.selective.isNull) { 130 formattedWrite(app, " selective '%(%s, %)'", mem.selective.get()); 131 } 132 if(!mem.members.isNull) { 133 formattedWrite(app, " members '%(%s, %)'" 134 , mem.members.get().map!(m => m.toString())); 135 } 136 app.put("\""); 137 return app.data; 138 } 139 140 struct Ast { 141 Module[] modules; 142 } 143 144 Ast parse(string filename) { 145 import std.file : readText; 146 147 JSONValue jv = parseJSON(readText(filename)); 148 return Ast(parseJson!(Module[])(jv)); 149 } 150 151 T parseJson(T)(JSONValue jv) { 152 static if(isBasicType!T) { 153 return jv.get!T(); 154 } else static if(isSomeString!T) { 155 return jv.get!string(); 156 } else static if(isArray!T && !isSomeString!T) { 157 enforce(jv.type == JSONType.array, format("Expected array not '%s'", 158 jv.type)); 159 T arr; 160 alias ET = ElementEncodingType!T; 161 //pragma(msg, T.stringof ~ " " ~ ET.stringof); 162 foreach(it; jv.arrayNoRef()) { 163 auto tmp = parseJson!ET(it); 164 static if(is(ET == Member) || is(ET == Module)) { 165 if(!tmp.name.startsWith("__unittest_")) { 166 arr ~= tmp; 167 } 168 } else { 169 arr ~= tmp; 170 } 171 } 172 return arr; 173 } else static if(is(T : Nullable!G, G)) { 174 if(jv.type != JSONType.null_) { 175 return nullable(parseJson!G(jv)); 176 } else { 177 return Nullable!(G).init; 178 } 179 } else static if(is(T == struct)) { 180 enforce(jv.type == JSONType.object, format("Expected object '%s' not '%s'\n%s", 181 T.stringof, jv.type, jv.toPrettyString())); 182 T ret; 183 184 string[] jsNames = jv.objectNoRef().keys().sort.array; 185 string[] sNames = ([FieldNameTuple!T] ~ ["endchar", "endline", "char", "line"]) 186 .map!(it => it.endsWith("_") ? it[0 .. $ - 1] : it) 187 .array.sort.array; 188 auto sd = setDifference(jsNames, sNames); 189 if(!sd.empty) { 190 writefln("%s", sd); 191 } 192 193 static foreach(mem; FieldNameTuple!T) {{ 194 alias MT = typeof(__traits(getMember, T, mem)); 195 //pragma(msg, T.stringof ~ " " ~ mem ~ " " ~ MT.stringof); 196 197 enum memNoPostfix = mem.endsWith("_") ? mem[0 .. $ - 1] : mem; 198 199 auto p = memNoPostfix in jv; 200 static if(is(MT : Nullable!F, F)) { 201 if(p !is null) { 202 __traits(getMember, ret, mem) = parseJson!(F)(*p); 203 } 204 } else { 205 enforce(p !is null, format("Couldn't find '%s'\n%s", mem, 206 jv.toPrettyString())); 207 __traits(getMember, ret, mem) = parseJson!MT(*p); 208 } 209 }} 210 return ret; 211 } 212 } 213 214 unittest { 215 import std.file; 216 217 foreach(f; dirEntries("testdirgen/", "*.json", SpanMode.depth)) { 218 try { 219 auto a = parse(f.name); 220 } catch(Exception e) { 221 assert(false, format("%s\n%s", f.name, e)); 222 } 223 } 224 }