1 module djinn.types; 2 3 @safe: 4 5 import std.array; 6 import std.algorithm; 7 import std.exception; 8 import std.range; 9 import std.utf; 10 11 /// Avoids autodecoding 12 package alias string8b = typeof("".byCodeUnit); 13 14 /// Tracks position in source code 15 struct Pos 16 { 17 const(Source)* src; 18 size_t offset; 19 20 size_t getLineNum() const pure 21 { 22 return src.getLineNum(offset); 23 } 24 } 25 26 /// A source code string with information about it 27 struct Source 28 { 29 this(string fname, string orig) pure 30 { 31 import std..string : lineSplitter; 32 this.fname = fname; 33 this.orig = rem = orig.byCodeUnit; 34 // The byte offsets of the start/end of each line are used for calculating line numbers 35 line_offsets = chain(only(0), lineSplitter!(Yes.keepTerminator)(orig).map!(l => l.length).cumulativeFold!"a+b").array; 36 } 37 38 bool empty() const pure 39 { 40 return rem.empty; 41 } 42 43 size_t length() const pure 44 { 45 return rem.length; 46 } 47 48 // Returns current 0-based byte offset into source 49 size_t offset() const pure 50 { 51 return orig.length - rem.length; 52 } 53 54 Pos curPos() return const pure 55 { 56 return posAt(offset); 57 } 58 59 /// Converts 0-based byte offset to Pos 60 Pos posAt(size_t o) return const pure 61 { 62 return Pos(&this, o); 63 } 64 65 // Maps 0-based byte offset to 1-based line number 66 size_t getLineNum(size_t o = offset()) const pure 67 { 68 // FIXME: support amortised O(line_offsets.length/tokens.length) approach 69 assert (!line_offsets.empty); 70 auto lines_after = assumeSorted(line_offsets).upperBound(o); 71 return line_offsets.length - lines_after.length; 72 } 73 74 const(string) fname; 75 const(string8b) orig; 76 77 package: 78 string8b rem; 79 const(size_t[]) line_offsets; 80 } 81 82 /// For Djinn syntax errors 83 class SyntaxException : Exception 84 { 85 mixin basicExceptionCtors; 86 } 87 88 package: 89 90 enum TokenType 91 { 92 text = 0, 93 statements, 94 expressions, 95 directive, 96 } 97 98 /// Jinja2 supports using - to indicate that whitespace should be stripped from the given side of the token 99 enum WhitespaceStripping 100 { 101 none = 0, 102 left = 1, 103 right = 2, 104 both = left | right, 105 } 106 107 /// Pieces of Djinn source code 108 struct Token 109 { 110 TokenType type; 111 string value; 112 Pos pos; 113 WhitespaceStripping whitespace_stripping; 114 } 115 116 alias TokenSink = Appender!(Token[]); 117 118 SyntaxException syntaxException(string msg, const(Source)* src, string file = __FILE__, size_t line = __LINE__) pure 119 { 120 const pos = src.curPos(); 121 return syntaxException(msg, pos, file, line); 122 } 123 124 SyntaxException syntaxException(string msg, ref const(Pos) pos, string file = __FILE__, size_t line = __LINE__) pure 125 { 126 import std.format : format; 127 return new SyntaxException(format("%s(%d): %s", pos.src.fname, pos.getLineNum(), msg), file, line); 128 }