1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Lex
10 {
11 public List<int> ComputeLineStartIndeces(const ustring& text)
12 {
13 List<int> indeces;
14 indeces.Add(0);
15 int state = 0;
16 int n = cast<int>(text.Length());
17 for (int i = 0; i < n; ++i;)
18 {
19 uchar c = text[i];
20 switch (state)
21 {
22 case 0:
23 {
24 indeces.Add(i);
25 if (c != '\n')
26 {
27 state = 1;
28 }
29 break;
30 }
31 case 1:
32 {
33 if (c == '\n')
34 {
35 state = 0;
36 }
37 break;
38 }
39 }
40 }
41 indeces.Add(n);
42 return indeces;
43 }
44
45 public class SourceFile
46 {
47 public SourceFile(ustring&& content_, List<int>&& lineStartIndeces_) : content(content_), lineStartIndeces(lineStartIndeces_)
48 {
49 }
50 public inline const ustring& Content() const
51 {
52 return content;
53 }
54 public inline uchar* Begin() const
55 {
56 return content.Chars();
57 }
58 public inline uchar* End() const
59 {
60 return content.Chars() + content.Length();
61 }
62 public inline const List<int>& LineStartIndeces() const
63 {
64 return lineStartIndeces;
65 }
66 [nodiscard]
67 public Result<string> GetLine(int lineNumber) const
68 {
69 int start = lineStartIndeces[lineNumber];
70 int len = -1;
71 if (lineNumber < lineStartIndeces.Count() - 1)
72 {
73 len = lineStartIndeces[lineNumber + 1] - start;
74 }
75 ustring line;
76 if (len != -1)
77 {
78 line = content.Substring(start, len);
79 }
80 else
81 {
82 line = content.Substring(start);
83 }
84 ustring trimmedLine = TrimEnd(line);
85 auto result = ToUtf8(trimmedLine);
86 if (result.Error())
87 {
88 return Result<string>(ErrorId(result.GetErrorId()));
89 }
90 return Result<string>(result.Value());
91 }
92 private ustring content;
93 private List<int> lineStartIndeces;
94 }
95
96 public class FileMap
97 {
98 public FileMap()
99 {
100 }
101 public int MapFileName(const string& fileName)
102 {
103 int fileIndex = cast<int>(fileNames.Count());
104 fileNames.Add(fileName);
105 return fileIndex;
106 }
107 public bool HasFileName(int fileIndex) const
108 {
109 return fileIndex >= 0 && fileIndex < fileNames.Count();
110 }
111 public const List<string>& FileNames() const
112 {
113 return fileNames;
114 }
115 public const string& GetFileName(int fileIndex) const
116 {
117 return fileNames[fileIndex];
118 }
119 public void AddSourceFile(int fileIndex, ustring&& content, List<int>&& lineStartIndeces)
120 {
121 sourceFileMap[fileIndex] = SourceFile(content, lineStartIndeces);
122 }
123 public SourceFile* GetSourceFile(int fileIndex) const
124 {
125 auto it = sourceFileMap.Find(fileIndex);
126 if (it != sourceFileMap.End())
127 {
128 return &(it->second);
129 }
130 else
131 {
132 return null;
133 }
134 }
135 public Result<SourceFile*> GetOrReadSourceFile(int fileIndex) const
136 {
137 SourceFile* sourceFile = GetSourceFile(fileIndex);
138 if (sourceFile != null)
139 {
140 return Result<SourceFile*>(sourceFile);
141 }
142 else
143 {
144 auto readResult = System.IO.File.ReadAllText(fileNames[fileIndex]);
145 if (readResult.Error()) return Result<SourceFile*>(ErrorId(readResult.GetErrorId()));
146 const string& content = readResult.Value();
147 auto utf32Result = ToUtf32(content);
148 if (utf32Result.Error()) return Result<SourceFile*>(ErrorId(utf32Result.GetErrorId()));
149 ustring text = Rvalue(utf32Result.Value());
150 List<int> lineStartIndeces = ComputeLineStartIndeces(text);
151 AddSourceFile(fileIndex, Rvalue(text), Rvalue(lineStartIndeces));
152 return GetSourceFile(fileIndex);
153 }
154 }
155 private List<string> fileNames;
156 private Map<int, SourceFile> sourceFileMap;
157 }
158
159 [nodiscard]
160 public Result<string> MakeMessage(const string& message, const Span& span, int fileIndex, FileMap& fileMap)
161 {
162 string msg = message;
163 string fileName;
164 if (fileMap.HasFileName(fileIndex))
165 {
166 fileName = fileMap.GetFileName(fileIndex);
167 }
168 SourceFile* sourceFile = fileMap.GetSourceFile(fileIndex);
169 if (sourceFile != null)
170 {
171 LineColLen lineColLen = SpanToLineColLen(span, sourceFile->LineStartIndeces());
172 System.IO.StringWriter writer;
173 auto lineResult = sourceFile->GetLine(lineColLen.line);
174 if (lineResult.Error())
175 {
176 return Result<string>(ErrorId(lineResult.GetErrorId()));
177 }
178 writer << message << ": " << fileName << ":" << lineColLen.line << ":\n" << lineResult.Value() << "\n" <<
179 string(' ', lineColLen.col - 1) << string('^', lineColLen.len) << endl();
180 msg = writer.GetString();
181 }
182 return Result<string>(msg);
183 }
184 }