1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity high) 4 module dil.SourceText; 5 6 import dil.i18n.Messages; 7 import dil.Converter, 8 dil.Diagnostics; 9 import util.Path; 10 import common; 11 12 import std.file; 13 14 /// Represents D source code. 15 /// 16 /// The source text may come from a file or from a memory buffer. 17 final class SourceText 18 { 19 /// The file path to the source text. Mainly used for error messages. 20 cstring filePath; 21 cstring data; /// The UTF-8, zero-terminated source text. 22 /// The data member must be terminated with this string. 23 /// Four zeros are used to make certain optimizations possible in the Lexer. 24 static immutable sentinelString = "\0\0\0\0"; 25 /// True when the text has no invalid UTF8 sequences. 26 //bool isValidUTF8; // TODO: could this be useful? 27 28 /// Constructs a SourceText object. 29 /// Params: 30 /// filePath = File path to the source file. 31 /// loadFile = Whether to load the file in the constructor. 32 this(cstring filePath, bool loadFile = false) 33 { 34 this.filePath = filePath; 35 loadFile && load(); 36 } 37 38 /// Constructs a SourceText object. 39 /// Params: 40 /// filePath = File path for error messages. 41 /// data = Memory buffer (may be terminated with sentinelString.) 42 this(cstring filePath, cstring data) 43 { 44 this(filePath); 45 addSentinelString(data); 46 this.data = data; 47 } 48 49 /// Returns a slice to the source text, excluding the sentinel string. 50 cstring text() 51 { 52 return data[0..$-4]; 53 } 54 55 /// Loads the source text from a file. 56 /// Returns: true for success, false on failure. 57 bool load(Diagnostics diag = null) 58 { 59 if (!diag) 60 diag = new Diagnostics(); 61 assert(filePath.length); 62 63 scope(failure) 64 { 65 auto loc = new Location(filePath, 0); 66 auto mid = Path(this.filePath).exists() ? 67 MID.CantReadFile : MID.InexistantFile; 68 diag ~= new LexerError(loc, diag.msg(mid)); 69 data = sentinelString.dup; 70 return false; 71 } 72 73 // Read the file. 74 auto rawdata = cast(ubyte[])filePath.read(); 75 // Convert the data. 76 auto converter = Converter(filePath, diag); 77 cstring text = converter.data2UTF8(rawdata); 78 addSentinelString(text); 79 this.data = text; 80 return true; 81 } 82 83 /// Appends the sentinel string to the text (if not already there.) 84 static void addSentinelString(ref cstring text) 85 { 86 if (text.length < 4 || 87 // Same as: text[$-4..$] != sentinelString 88 *cast(uint*)(text.ptr+text.length-4) != 0) 89 text ~= sentinelString; 90 } 91 }