1 /**** Parser after grammar splitting. 2 * 3 * Author: ARaspiK 4 * License: MIT 5 */ 6 module sdlangp.parser; 7 8 import pegged.grammar, sdlangp.grammar; 9 import sdlangp.node; 10 11 import std.array; 12 import std.datetime; 13 import std.algorithm; 14 import std.exception; 15 import std.base64; 16 import std.conv; 17 import std.bigint; 18 import sumtype; 19 20 /// Value parser for our custom grammar. 21 @trusted Value parseValue(ParseTree node) { 22 // Get the actual type if not already done so. 23 if (node.name == "SDL.Value") 24 node = node.children[0]; 25 26 // Simpler access for most value types, by first match string. 27 string data = node.matches[0]; 28 29 // Switch by name (excluding "SDL.") 30 switch (node.name[4 .. $]) { 31 case "String": 32 return Value(data.idup); 33 case "Number": 34 // ...: 32-bit 35 // ...L: 64-bit 36 // ...BD: 128-bit 37 return data[$-1] == 'L' 38 ? Value(data[0 .. $-1].to!long) 39 : (data.length > 2 && data[$-2] == 'B') 40 ? Value(BigInt(data[0 .. $-2])) 41 : Value(data.to!int); 42 case "Float": 43 // ...: 64-bit 44 // ...f: 32-bit 45 return data[$-1] == 'f' 46 ? Value(data[0 .. $-1].to!float) 47 : Value(data.to!double); 48 case "Boolean": 49 // "on", "off", "true", "false" 50 return Value(data == "true" || data == "on"); 51 case "Null": 52 // "null" 53 return Value(null); 54 case "DateTime": 55 // YYYY/MM/DD HH:MM:SS.FFF(-UTC)? => YYYYMMDD HHMMSS FFF (-UTC)? 56 auto date = DateTime.min; 57 auto frac = Duration.zero; 58 bool utc = false; 59 foreach_reverse(i, s; node.matches) switch (i) { 60 case 3: 61 utc = true; 62 break; 63 case 2: 64 frac = s.to!size_t.msecs; 65 break; 66 case 1: 67 date.timeOfDay = TimeOfDay.fromISOString(s); 68 break; 69 case 0: 70 date.date = Date.fromISOString(s); 71 break; 72 default: assert(0); 73 } 74 return Value(SysTime(date, frac, utc ? UTC() : null)); 75 case "Duration": 76 // (DDd:)?HH:MM:SS(.FFF)? => (DDd)? HH:MM:SS (.FFF)? 77 auto d = Duration.zero; 78 foreach (s; node.matches) { 79 if (s[$-1] == 'd') 80 d += s[0 .. $-1].to!size_t.days; 81 else if (s[0] == '.') 82 d += s[1 .. $].to!size_t.msecs; 83 else 84 d += TimeOfDay.fromISOString(s) - TimeOfDay.min; 85 } 86 return Value(d); 87 case "Base64": 88 // [base64stuff] => base64stuff 89 return Value(Base64Impl!('+', '/', Base64.NoPadding).decode(data)); 90 default: 91 assert(false, "No such value type!"); 92 } 93 } 94 95 /// Custom parser for node trees. 96 @safe Node*[] parseTree(ParseTree node) nothrow { 97 return node.children.map!(l => l.children.map!parseNode).join; 98 } 99 100 /// Custom parser for single nodes. 101 @safe Node* parseNode(ParseTree node) nothrow { 102 Node* res = new Node(null); 103 104 foreach (t; node.children) switch (t.name[4 .. $]) { 105 case "TagName": 106 res.name = t.matches[$-1]; 107 res.namespace = t.matches.length == 2 ? t.matches[0] : ""; 108 break; 109 case "Value": 110 res.values ~= t.parseValue.assumeWontThrow; 111 break; 112 case "Attribute": 113 res.attrs[t.children[0].matches[0]] = 114 t.children[1].parseValue.assumeWontThrow; 115 break; 116 case "TagTree": 117 res.children = t.parseTree; 118 break; 119 default: assert(0); 120 } 121 122 return res; 123 }