[fidl][errors] Print column numbers and underline unexpected tokens

Change-Id: I1c15e58e23c7bdf7c92b6e7691db9242e5cd06cb
diff --git a/system/host/fidl/include/fidl/source_file.h b/system/host/fidl/include/fidl/source_file.h
index 644f405..8b9f9d5 100644
--- a/system/host/fidl/include/fidl/source_file.h
+++ b/system/host/fidl/include/fidl/source_file.h
@@ -21,7 +21,14 @@
     StringView filename() const { return filename_; }
     StringView data() const { return data_; }
 
-    StringView LineContaining(StringView view, int* line_number_out) const;
+    // This is in the coordinates that most editors use. Lines start
+    // at 1 but columns start at 0.
+    struct Position {
+        int line;
+        int column;
+    };
+
+    StringView LineContaining(StringView view, Position* position_out) const;
 
 private:
     std::string filename_;
diff --git a/system/host/fidl/include/fidl/source_location.h b/system/host/fidl/include/fidl/source_location.h
index 198282d..6f98918 100644
--- a/system/host/fidl/include/fidl/source_location.h
+++ b/system/host/fidl/include/fidl/source_location.h
@@ -29,7 +29,7 @@
     const StringView& data() const { return data_; }
     const SourceFile& source_file() const { return *source_file_; }
 
-    StringView SourceLine(int* line_number_out) const;
+    StringView SourceLine(SourceFile::Position* position_out) const;
 
 private:
     StringView data_;
diff --git a/system/host/fidl/lib/parser.cpp b/system/host/fidl/lib/parser.cpp
index 2077c24..84ae72c 100644
--- a/system/host/fidl/lib/parser.cpp
+++ b/system/host/fidl/lib/parser.cpp
@@ -79,15 +79,23 @@
 
 decltype(nullptr) Parser::Fail() {
     if (ok_) {
-        int line_number;
-        auto surrounding_line = last_token_.location().SourceLine(&line_number);
+        auto token_location = last_token_.location();
+        auto token_data = token_location.data();
 
-        std::string error = "found unexpected token: ";
-        error += last_token_.data();
-        error += "\n";
-        error += "on line #" + std::to_string(line_number) + ":\n\n";
+        SourceFile::Position position;
+        std::string surrounding_line = token_location.SourceLine(&position);
+        auto line_number = std::to_string(position.line);
+        auto column_number = std::to_string(position.column);
+
+        std::string squiggle(position.column, ' ');
+        squiggle +="^";
+        squiggle += std::string(token_data.size() - 1, '~');
+
+        std::string error = "found unexpected token on ";
+        error += "line " + line_number;
+        error += " column " + column_number + ":\n";
         error += surrounding_line;
-        error += "\n";
+        error += squiggle + "\n";
 
         error_reporter_->ReportError(error);
         ok_ = false;
diff --git a/system/host/fidl/lib/source_file.cpp b/system/host/fidl/lib/source_file.cpp
index 974892c..5eed0f4 100644
--- a/system/host/fidl/lib/source_file.cpp
+++ b/system/host/fidl/lib/source_file.cpp
@@ -31,7 +31,7 @@
 
 SourceFile::~SourceFile() = default;
 
-StringView SourceFile::LineContaining(StringView view, int* line_number_out) const {
+StringView SourceFile::LineContaining(StringView view, Position* position_out) const {
     auto ptr_order = [](const char* left, const char* right) {
         return std::less_equal<const char*>()(left, right);
     };
@@ -50,9 +50,12 @@
     auto line = std::find_if(lines_.cbegin(), lines_.cend(), is_in_line);
     assert(line != lines_.cend());
 
-    if (line_number_out != nullptr) {
+    if (position_out != nullptr) {
         // Humans number lines from 1.
-        *line_number_out = (line - lines_.cbegin()) + 1;
+        int line_number = (line - lines_.cbegin()) + 1;
+        // But columns from 0!
+        int column_number = view.data() - line->data();
+        *position_out = {line_number, column_number};
     }
     return *line;
 }
diff --git a/system/host/fidl/lib/source_location.cpp b/system/host/fidl/lib/source_location.cpp
index abe0718..ae7c6c2 100644
--- a/system/host/fidl/lib/source_location.cpp
+++ b/system/host/fidl/lib/source_location.cpp
@@ -6,8 +6,8 @@
 
 namespace fidl {
 
-StringView SourceLocation::SourceLine(int* line_number_out) const {
-    return source_file_->LineContaining(data(), line_number_out);
+StringView SourceLocation::SourceLine(SourceFile::Position* position_out) const {
+    return source_file_->LineContaining(data(), position_out);
 }
 
 } // namespace fidl