1 /* 2 * This file is part of gir-to-d. 3 * 4 * gir-to-d is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation, either version 3 7 * of the License, or (at your option) any later version. 8 * 9 * gir-to-d is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with gir-to-d. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 module gtd.DefReader; 19 20 import std.algorithm; 21 import std.array; 22 import std.conv : hexString; 23 import std.file; 24 import std.string : splitLines, strip, indexOf; 25 26 import gtd.WrapException; 27 28 public final class DefReader 29 { 30 string fileName; 31 string key; 32 string subKey; 33 string value; 34 35 private size_t _lineNumber; 36 private size_t lineOffset; 37 private string[] lines; 38 39 public this(string fileName) 40 { 41 this.fileName = fileName; 42 43 lines = readText(fileName).splitLines(); 44 //Skip utf8 BOM. 45 lines[0].skipOver(hexString!"efbbbf"); 46 47 this.popFront(); 48 } 49 50 /** 51 * Proccess the _lines defined in lines. 52 * The fileName and lineOffset are only used for error reporting. 53 */ 54 public this(string[] lines, string fileName = "", size_t lineOffset = 0) 55 { 56 this.lines = lines; 57 this.fileName = fileName; 58 this.lineOffset = lineOffset; 59 this.popFront(); 60 } 61 62 public void popFront() 63 { 64 string line; 65 66 if ( !lines.empty ) 67 { 68 line = lines.front.strip(); 69 lines.popFront(); 70 _lineNumber++; 71 72 while ( !lines.empty && ( line.empty || line.startsWith("#") ) ) 73 { 74 line = lines.front.strip(); 75 lines.popFront(); 76 _lineNumber++; 77 } 78 } 79 80 if ( !line.empty && !line.startsWith("#") ) 81 { 82 ptrdiff_t index = line.indexOf(':'); 83 84 key = line[0 .. max(index, 0)].strip(); 85 value = line[index +1 .. $].strip(); 86 subKey = ""; 87 88 index = key.indexOf(' '); 89 if ( index != -1 ) 90 { 91 subKey = key[index +1 .. $].strip(); 92 key = key[0 .. index].strip(); 93 } 94 } 95 else 96 { 97 key.length = 0; 98 value.length = 0; 99 subKey.length = 0; 100 } 101 } 102 103 /** 104 * Gets the content of a block value 105 */ 106 public string[] readBlock(string key = "") 107 { 108 string[] block; 109 110 if ( key.empty ) 111 key = this.key; 112 113 while ( !lines.empty ) 114 { 115 if ( startsWith(lines.front.strip(), key) ) 116 { 117 lines.popFront(); 118 _lineNumber++; 119 return block; 120 } 121 122 block ~= lines.front ~ '\n'; 123 lines.popFront(); 124 _lineNumber++; 125 } 126 127 throw new LookupException(this, "Found EOF while expecting: \""~key~": end\""); 128 } 129 130 /** 131 * Skip the content of a block. Supports nested blocks. 132 */ 133 public void skipBlock(string key = "") 134 { 135 if ( key.empty ) 136 key = this.key; 137 138 size_t nestedBlocks = 1; 139 do 140 { 141 do lines.popFront; while ( !lines.front.strip().startsWith(key) ); 142 143 if ( lines.front.strip().endsWith("start") ) 144 nestedBlocks++; 145 else if ( lines.front.strip().endsWith("end") ) 146 nestedBlocks--; 147 } 148 while ( nestedBlocks > 0 ); 149 } 150 151 /** 152 * Gets the current value as a bool 153 */ 154 public @property bool valueBool() const 155 { 156 return !!value.among("1", "ok", "OK", "Ok", "true", "TRUE", "True", "Y", "y", "yes", "YES", "Yes"); 157 } 158 159 public @property bool empty() const 160 { 161 return lines.empty && key.empty; 162 } 163 164 public @property size_t lineNumber() const 165 { 166 return _lineNumber + lineOffset; 167 } 168 } 169 170 class LookupException : WrapException 171 { 172 this(DefReader defReader, string msg) 173 { 174 super(msg, defReader.fileName, defReader.lineNumber); 175 } 176 }