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 Nullable!(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 private bool nameIsOkayToUse(T)(T obj) { 152 static if(is(typeof(obj.name) : Nullable!F, F)) { 153 return !obj.name.isNull(); 154 } else { 155 return true; 156 } 157 } 158 159 T parseJson(T)(JSONValue jv) { 160 static if(isBasicType!T) { 161 return jv.get!T(); 162 } else static if(isSomeString!T) { 163 return jv.get!string(); 164 } else static if(isArray!T && !isSomeString!T) { 165 enforce(jv.type == JSONType.array, format("Expected array not '%s'", 166 jv.type)); 167 T arr; 168 alias ET = ElementEncodingType!T; 169 //pragma(msg, T.stringof ~ " " ~ ET.stringof); 170 foreach(it; jv.arrayNoRef()) { 171 auto tmp = parseJson!ET(it); 172 static if(is(ET == Member) || is(ET == Module)) { 173 if(!tmp.nameIsOkayToUse()) { 174 continue; 175 } 176 if(!tmp.name.startsWith("__unittest_")) { 177 arr ~= tmp; 178 } else if(!tmp.name.startsWith("__unittest_")) { 179 arr ~= tmp; 180 } 181 } else { 182 arr ~= tmp; 183 } 184 } 185 return arr; 186 } else static if(is(T : Nullable!G, G)) { 187 if(jv.type != JSONType.null_) { 188 return nullable(parseJson!G(jv)); 189 } else { 190 return Nullable!(G).init; 191 } 192 } else static if(is(T == struct)) { 193 enforce(jv.type == JSONType.object, format("Expected object '%s' not '%s'\n%s", 194 T.stringof, jv.type, jv.toPrettyString())); 195 T ret; 196 197 string[] jsNames = jv.objectNoRef().keys().sort.array; 198 string[] sNames = ([FieldNameTuple!T] ~ ["endchar", "endline", "char", "line"]) 199 .map!(it => it.endsWith("_") ? it[0 .. $ - 1] : it) 200 .array.sort.array; 201 auto sd = setDifference(jsNames, sNames); 202 if(!sd.empty) { 203 writefln("%s", sd); 204 } 205 206 static foreach(mem; FieldNameTuple!T) {{ 207 alias MT = typeof(__traits(getMember, T, mem)); 208 //pragma(msg, T.stringof ~ " " ~ mem ~ " " ~ MT.stringof); 209 210 enum memNoPostfix = mem.endsWith("_") ? mem[0 .. $ - 1] : mem; 211 212 auto p = memNoPostfix in jv; 213 static if(is(MT : Nullable!F, F)) { 214 if(p !is null) { 215 __traits(getMember, ret, mem) = parseJson!(F)(*p); 216 } 217 } else { 218 enforce(p !is null, format("Couldn't find '%s'\n%s", mem, 219 jv.toPrettyString())); 220 __traits(getMember, ret, mem) = parseJson!MT(*p); 221 } 222 }} 223 return ret; 224 } 225 } 226 227 unittest { 228 import std.file; 229 230 foreach(f; dirEntries("testdirgen/", "*.json", SpanMode.depth)) { 231 try { 232 auto a = parse(f.name); 233 } catch(Exception e) { 234 assert(false, format("%s\n%s", f.name, e)); 235 } 236 } 237 }