1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "banjo/source_file.h"
6 
7 #include <assert.h>
8 
9 #include <algorithm>
10 #include <functional>
11 
12 namespace banjo {
13 
SourceFile(std::string filename,std::string data)14 SourceFile::SourceFile(std::string filename, std::string data)
15     : filename_(std::move(filename)), data_(std::move(data)) {
16     size_t size = 0u;
17     auto start_of_line = data_.cbegin();
18 
19     for (auto it = data_.cbegin(); it != data_.cend(); ++it) {
20         ++size;
21         if (*it == '\n' || *it == '\0') {
22             auto& position = *start_of_line;
23             lines_.push_back(StringView(&position, size));
24 
25             size = 0u;
26             start_of_line = it + 1;
27         }
28     }
29 }
30 
31 SourceFile::~SourceFile() = default;
32 
LineContaining(StringView view,Position * position_out) const33 StringView SourceFile::LineContaining(StringView view, Position* position_out) const {
34     auto ptr_less_equal = std::less_equal<const char*>();
35 
36     assert(ptr_less_equal(data().data(), view.data()) && "The view is not part of this SourceFile");
37     assert(ptr_less_equal(view.data() + view.size(), data().data() + data().size()) &&
38            "The view is not part of this SourceFile");
39 
40     // We are looking from the end of the file backwards (hence
41     // crbegin and crend), looking for the first line (hence
42     // upper_bound) to start at or before before the token in
43     // question.
44     auto is_in_line = [&ptr_less_equal](const StringView& left, const StringView& right) {
45         return ptr_less_equal(right.data(), left.data());
46     };
47     auto line = std::upper_bound(lines_.crbegin(), lines_.crend(), view, is_in_line);
48     assert(line != lines_.crend());
49 
50     if (position_out != nullptr) {
51         // Humans number lines from 1. Calculating this from the end
52         // accounts for this.
53         int line_number = lines_.crend() - line;
54         // But columns from 0!
55         int column_number = view.data() - line->data();
56         *position_out = {line_number, column_number};
57     }
58     return *line;
59 }
60 
61 } // namespace banjo
62