diff --git a/src/Diagnostics/DiagnosticsHolder.hpp b/src/Diagnostics/DiagnosticsHolder.hpp index 7474433..ee85418 100644 --- a/src/Diagnostics/DiagnosticsHolder.hpp +++ b/src/Diagnostics/DiagnosticsHolder.hpp @@ -3,6 +3,7 @@ #define PORYGONLANG_DIAGNOSTICSHOLDER_HPP #include +#include #include "DiagnosticSeverity.hpp" #include "DiagnosticCode.hpp" #include "Diagnostic.hpp" @@ -13,9 +14,21 @@ namespace Porygon::Diagnostics { class DiagnosticsHolder { bool _hasErrors; vector _diagnostics; + vector _lineStarts; + vector _lineLength; public: - DiagnosticsHolder() { + explicit DiagnosticsHolder(const u16string& str) { _hasErrors = false; + _lineStarts = vector{0}; + size_t lineLength = 0; + for (size_t i = 0; i < str.size(); i++){ + lineLength++; + if (str[i] == '\n'){ + _lineStarts.push_back(i + 1); + _lineLength.push_back(lineLength); + lineLength = 0; + } + } } ~DiagnosticsHolder() { @@ -37,6 +50,29 @@ namespace Porygon::Diagnostics { int DiagnosticsCount(); Diagnostic *GetDiagnosticAt(int position); + + size_t GetLineFromPosition(size_t i){ + size_t topLimit = _lineStarts.size() - 1; + size_t bottomLimit = 0; + while (true){ + if (bottomLimit == topLimit){ + return bottomLimit; + } + size_t half = (topLimit - bottomLimit) / 2; + size_t pos = _lineStarts[half]; + size_t length = _lineLength[half]; + if (pos < i && pos + length > i){ + return half; + } + if (pos > i){ + bottomLimit = half; + } else if (pos < i){ + topLimit = half; + } else{ + return half; + } + } + } }; } diff --git a/src/Script.cpp b/src/Script.cpp index 0749b42..684e2a5 100644 --- a/src/Script.cpp +++ b/src/Script.cpp @@ -10,9 +10,7 @@ #include "Binder/Binder.hpp" Porygon::Script* Porygon::Script::Create(const u16string& script) { - auto s = new Script(); - s -> Parse(script); - return s; + return new Script(script); } std::u16string To_UTF16(const string &s) @@ -24,11 +22,13 @@ Porygon::Script *Porygon::Script::Create(const string &script) { return Script::Create(To_UTF16(script)); } -Porygon::Script::Script() { - Diagnostics = new Diagnostics::DiagnosticsHolder(); +Porygon::Script::Script(const u16string& s) { + Diagnostics = new Diagnostics::DiagnosticsHolder(s); _boundScript = nullptr; _scriptVariables = new unordered_map>(0); _evaluator = new Evaluator(this -> _scriptVariables); + + this -> Parse(s); } EvalValue* Porygon::Script::Evaluate() { diff --git a/src/Script.hpp b/src/Script.hpp index eb09deb..414d350 100644 --- a/src/Script.hpp +++ b/src/Script.hpp @@ -22,7 +22,7 @@ namespace Porygon{ Binder::BoundScriptStatement* _boundScript; shared_ptr _returnType; - explicit Script(); + explicit Script(const u16string&); void Parse(const u16string& script); public: static Script* Create(const u16string& script); diff --git a/tests/integration/DiagnosticsTests.cpp b/tests/integration/DiagnosticsTests.cpp index 1ab716d..530f919 100644 --- a/tests/integration/DiagnosticsTests.cpp +++ b/tests/integration/DiagnosticsTests.cpp @@ -23,8 +23,24 @@ TEST_CASE( "Diagnostic invalid token", "[integration]" ) { CHECK(diags[0].GetCode() == Diagnostics::DiagnosticCode::UnexpectedToken); CHECK(diags[0].GetStartPosition() == 3); CHECK(diags[0].GetLength() == 1); + CHECK(script->Diagnostics->GetLineFromPosition(diags[0].GetStartPosition()) == 0); + delete script; +} + +TEST_CASE( "Get diagnostic line", "[integration]" ) { + auto script = Script::Create(uR"( +1 +/ 1 +)"); + REQUIRE(script->Diagnostics -> HasErrors()); + auto diags = script->Diagnostics -> GetDiagnostics(); + REQUIRE(diags.size() == 1); + CHECK(diags[0].GetCode() == Diagnostics::DiagnosticCode::UnexpectedToken); + CHECK(diags[0].GetStartPosition() == 4); + CHECK(diags[0].GetLength() == 1); + CHECK(script->Diagnostics->GetLineFromPosition(diags[0].GetStartPosition()) == 1); delete script; } + #endif \ No newline at end of file