1 module toml.parser; 2 3 import toml.grammar; 4 import pegged.grammar; 5 import std.array: split; 6 import std.exception; 7 import std.file : readText; 8 9 enum TOMLType { 10 String, 11 Integer, 12 Float, 13 Boolean, 14 Datetime, 15 Array, 16 Group 17 }; 18 19 class TOMLException: Exception { 20 this(string msg, string file="parser", ulong line=111) { 21 super(msg, file, line); 22 } 23 } 24 25 alias enforceTOML = enforceEx!(TOMLException); 26 27 struct TOMLValue { 28 29 union Store { 30 string stringv; 31 long intv; 32 float floatv; 33 bool boolv; 34 TOMLValue[] arrayv; 35 TOMLValue[string] keygroups; 36 } 37 38 private { 39 Store _store; 40 TOMLType _type; 41 } 42 43 private void assign(T)(T val) { 44 static if( is(T: string) ) { 45 _store.stringv = val; 46 _type = TOMLType.String; 47 } 48 else static if ( is(T: long) ) { 49 _store.intv = val; 50 _type = TOMLType.Integer; 51 } 52 else static if ( is(T: bool) ) { 53 _store.boolv = val; 54 _type = TOMLType.Boolean; 55 } 56 else static if ( is(T: float) ) { 57 _store.floatv = val; 58 _type = TOMLType.Float; 59 } 60 else 61 static assert(0, "Unknown type"); 62 63 } 64 65 private void assign(T)(T val, string key) { 66 static if ( is(T: TOMLValue) ) 67 _store.keygroups[key] = val; 68 else 69 _store.keygroups[key] = TOMLValue(val); 70 } 71 72 // 73 // Constructors 74 // ----------------------------------------- 75 76 this(T)(T v) { 77 static if ( is(T: TOMLType) ) 78 _type = v; 79 else 80 assign(v); 81 } 82 83 this(T)(ref T v) { 84 static if ( is(T: TOMLValue[]) ) { 85 _store.arrayv = v; 86 _type = TOMLType.Array; 87 } 88 else static if ( is(T: TOMLValue[string]) ) { 89 _store.keygroups = v; 90 _type = TOMLType.Group; 91 } 92 } 93 94 // 95 // Operators 96 // --------------------------------------- 97 98 // Index assign 99 void opIndexAssign(T)(T v, string key) { 100 enforceTOML(_type==TOMLType.Group); 101 assign(v, key); 102 } 103 104 TOMLValue opIndexAssign(TOMLValue v, string key) { 105 enforceTOML(_type==TOMLType.Group); 106 _store.keygroups[key] = v; 107 return v; 108 } 109 110 ref inout(TOMLValue) opIndex(string v) inout { 111 enforceTOML(_type==TOMLType.Group); 112 return _store.keygroups[v]; 113 } 114 115 auto opBinaryRight(string op : "in")(string k) const 116 { 117 enforceTOML(_type==TOMLType.Group); 118 return k in _store.keygroups; 119 } 120 121 122 // 123 // Value accessors 124 // --------------------------------------- 125 string str(){ 126 enforceTOML(_type==TOMLType.String); 127 return _store.stringv; 128 } 129 130 long integer() { 131 enforceTOML(_type==TOMLType.Integer); 132 return _store.intv; 133 } 134 135 TOMLValue[] array() { 136 enforceTOML(_type==TOMLType.Array); 137 return _store.arrayv; 138 } 139 140 TOMLValue[string] group() { 141 enforceTOML(_type==TOMLType.Group); 142 return _store.keygroups; 143 } 144 145 auto keys() { 146 enforceTOML(_type==TOMLType.Group); 147 return _store.keygroups.keys; 148 } 149 150 } 151 152 void _toTOMLDictionary(ParseTree p, ref TOMLValue root, string current_header=null) { 153 154 TOMLValue __valueLine(ParseTree valueNode){ 155 auto v = valueNode.matches[0]; 156 switch (valueNode.name) { 157 case "TOML.IntegerValue": 158 return TOMLValue(v.to!int); 159 case "TOML.StringValue": 160 return TOMLValue(v.to!string); 161 case "TOML.FloatValue": 162 return TOMLValue(v.to!float); 163 case "TOML.BooleanValue": 164 return TOMLValue(v.to!bool); 165 case "TOML.Array": 166 TOMLValue[] vals; 167 //first children is the typed array match 168 foreach (pc; valueNode.children[0].children) { 169 vals ~= __valueLine(pc); 170 } 171 return TOMLValue(vals); 172 default: 173 break; 174 } 175 assert(0); 176 } 177 178 switch (p.name) { 179 case "TOML.ValueLine": 180 auto name = p.children[0].matches[0]; //Name node 181 auto value = p.children[1].children[0]; //Value node 182 if (current_header==null) 183 root[name] = __valueLine(value); 184 else { 185 auto k = current_header.split('.'); 186 if (!(k[0] in root)) root[k[0]] = TOMLValue(TOMLType.Group); 187 TOMLValue * v = &root[k[0]]; 188 189 foreach (t; k[1..$]) { 190 if (!(t in v._store.keygroups)) 191 v.assign(TOMLValue(TOMLType.Group), t); 192 v = &(v._store.keygroups[t]); 193 } 194 v._store.keygroups[name] = __valueLine(value); 195 196 } 197 break; 198 case "TOML.KeyGroup": 199 auto header = p.children[0].children[0].matches[0]; //HeaderName node 200 auto vals = p.children[1..$]; 201 foreach (v; vals) { 202 _toTOMLDictionary(v, root, header); 203 } 204 break; 205 default: 206 break; 207 } 208 } 209 210 TOMLValue parse(string data) { 211 auto parseTree = TOML(data); 212 TOMLValue dict = TOMLValue(TOMLType.Group); 213 foreach(p; parseTree.children[0].children) { 214 _toTOMLDictionary(p, dict); 215 } 216 return dict; 217 } 218 219 TOMLValue parseFile(string filename) { 220 return parse(readText(filename)); 221 } 222 223 unittest { 224 import std.stdio; 225 226 enum TEST1 = ` 227 key_string = "string_value" 228 key_array = ["string_1", "string_2"] 229 230 [servers] 231 a = 12 232 233 [servers.test] 234 a = 12 235 ports = [1,2,3] 236 `; 237 238 auto d = parse(TEST1); 239 240 assert(d["key_string"].str == "string_value"); 241 writefln("Servers: %s", d["servers"].keys); 242 writefln("All: %s", d.keys); 243 assert(d["servers"]["a"].integer == 12); 244 assert(d["servers"]["test"]["a"].integer == 12); 245 assert(d["servers"]["test"]["ports"].array[2].integer == 3); 246 247 }