1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity high)
4 module dil.Location;
5 
6 import dil.lexer.Funcs;
7 import dil.Unicode;
8 import common;
9 
10 /// Represents a location in a source text.
11 final class Location
12 {
13   cstring filePath; /// The file path.
14   size_t lineNum; /// The line number.
15   cchar* lineBegin, to; /// Used to calculate the column.
16 
17   static uint TAB_WIDTH = 4; /// The default width of the tabulator character.
18 
19   /// Forwards the parameters to the second constructor.
20   this(cstring filePath, size_t lineNum)
21   {
22     set(filePath, lineNum);
23   }
24 
25   /// Constructs a Location object.
26   this(cstring filePath, size_t lineNum, cchar* lineBegin, cchar* to)
27   {
28     set(filePath, lineNum, lineBegin, to);
29   }
30 
31   void set(cstring filePath, size_t lineNum)
32   {
33     set(filePath, lineNum, null, null);
34   }
35 
36   void set(cstring filePath, size_t lineNum, cchar* lineBegin, cchar* to)
37   {
38     this.filePath  = filePath;
39     set(lineNum, lineBegin, to);
40   }
41 
42   void set(size_t lineNum, cchar* lineBegin, cchar* to)
43   {
44     assert(lineBegin <= to);
45     this.lineNum   = lineNum;
46     this.lineBegin = lineBegin;
47     this.to        = to;
48   }
49 
50   void setFilePath(cstring filePath)
51   {
52     this.filePath = filePath;
53   }
54 
55   /// Uses a simple method to count the number of characters in a string.
56   ///
57   /// Note: Unicode compound characters and other special characters are not
58   /// taken into account.
59   /// Params:
60   ///   tabWidth = The width of the tabulator character.
61   uint calculateColumn(uint tabWidth = Location.TAB_WIDTH)
62   {
63     uint col;
64     auto p = lineBegin;
65     if (!p)
66       return 0;
67     for (; p <= to; p++)
68     {
69       assert(delegate ()
70         {
71           // Check that there is no newline between p and to.
72           // But 'to' may point to a newline.
73           if (p != to && isNewline(*p))
74             return false;
75           if (to-p >= 2 && isUnicodeNewline(p))
76             return false;
77           return true;
78         }() == true
79       );
80 
81       // Skip this byte if it is a trail byte of a UTF-8 sequence.
82       if (isTrailByte(*p))
83         continue; // *p == 0b10xx_xxxx
84 
85       // Only count ASCII characters and the first byte of a UTF-8 sequence.
86       if (*p == '\t')
87         col += tabWidth;
88       else
89         col++;
90     }
91     return col;
92   }
93   alias colNum = calculateColumn;
94 
95   char[] str(cstring format = "({},{})")
96   {
97     return Format(format, lineNum, colNum);
98   }
99 
100   char[] repr(cstring format = "{}({},{})")
101   {
102     return Format(format, filePath, lineNum, colNum);
103   }
104 }