2727#include < liblangutil/ErrorReporter.h>
2828#include < liblangutil/Exceptions.h>
2929#include < liblangutil/Scanner.h>
30+ #include < liblangutil/Common.h>
3031#include < libsolutil/Common.h>
3132#include < libsolutil/Visitor.h>
3233
33- #include < range/v3/view/subrange.hpp>
34-
3534#include < boost/algorithm/string.hpp>
3635
3736#include < algorithm>
@@ -52,7 +51,7 @@ std::optional<int> toInt(std::string const& _value)
5251 {
5352 return stoi (_value);
5453 }
55- catch (... )
54+ catch (std::out_of_range const & )
5655 {
5756 return std::nullopt ;
5857 }
@@ -192,13 +191,42 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
192191 langutil::SourceLocation const & _commentLocation
193192)
194193{
195- static std::regex const argsRegex = std::regex (
196- R"~~( ^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~" // index and location, e.g.: 1:234:-1
197- R"~~( ("(?:[^"\\]|\\.)*"?)?)~~" , // optional code snippet, e.g.: "string memory s = \"abc\";..."
198- std::regex_constants::ECMAScript | std::regex_constants::optimize
199- );
200- std::match_results<std::string_view::const_iterator> match;
201- if (!regex_search (_arguments.cbegin (), _arguments.cend (), match, argsRegex))
194+ CharStream argumentStream (std::string (_arguments), " " );
195+ Scanner scanner (argumentStream);
196+ scanner.setScannerMode (ScannerKind::SpecialComment);
197+
198+ std::string_view tail { _arguments.substr (_arguments.size ()) };
199+ auto const parseLocationComponent = [](Scanner& _scanner, bool expectTrailingColon) -> std::optional<std::string>
200+ {
201+ bool negative = false ;
202+ if (_scanner.currentToken () == Token::Sub)
203+ {
204+ negative = true ;
205+ _scanner.next ();
206+ }
207+ if (_scanner.currentToken () != Token::Number)
208+ return std::nullopt ;
209+ if (expectTrailingColon && _scanner.peekNextToken () != Token::Colon)
210+ return std::nullopt ;
211+ if (!isValidDecimal (_scanner.currentLiteral ()))
212+ return std::nullopt ;
213+ std::string decimal = (negative ? " -" : " " ) + _scanner.currentLiteral ();
214+ _scanner.next ();
215+ if (expectTrailingColon)
216+ _scanner.next ();
217+ return decimal;
218+ };
219+ std::optional<std::string> rawSourceIndex = parseLocationComponent (scanner, true );
220+ std::optional<std::string> rawStart = parseLocationComponent (scanner, true );
221+ std::optional<std::string> rawEnd = parseLocationComponent (scanner, false );
222+
223+ size_t const snippetStart = static_cast <size_t >(scanner.currentLocation ().start );
224+ bool const locationScannedSuccessfully = rawSourceIndex && rawStart && rawEnd;
225+ bool const locationIsWhitespaceSeparated =
226+ scanner.peekNextToken () == Token::EOS ||
227+ (snippetStart > 0 && langutil::isWhiteSpace (_arguments[snippetStart - 1 ]));
228+
229+ if (!locationScannedSuccessfully || !locationIsWhitespaceSeparated)
202230 {
203231 m_errorReporter.syntaxError (
204232 8387_error,
@@ -208,13 +236,12 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
208236 return std::nullopt ;
209237 }
210238
211- solAssert (match.size () == 5 , " " );
212- std::string_view tail = _arguments.substr (static_cast <size_t >(match.position () + match.length ()));
239+ if (scanner.currentToken () == Token::StringLiteral || (scanner.currentToken () == Token::Illegal && scanner.currentError () == ScannerError::IllegalStringEndQuote))
240+ tail = _arguments.substr (static_cast <size_t >(scanner.currentLocation ().end ));
241+ else
242+ tail = _arguments.substr (static_cast <size_t >(scanner.currentLocation ().start ));
213243
214- if (match[4 ].matched && (
215- !boost::algorithm::ends_with (match[4 ].str (), " \" " ) ||
216- boost::algorithm::ends_with (match[4 ].str (), " \\\" " )
217- ))
244+ if (scanner.currentToken () == Token::Illegal && scanner.currentError () == ScannerError::IllegalStringEndQuote)
218245 {
219246 m_errorReporter.syntaxError (
220247 1544_error,
@@ -224,30 +251,34 @@ std::optional<std::pair<std::string_view, SourceLocation>> Parser::parseSrcComme
224251 return {{tail, SourceLocation{}}};
225252 }
226253
227- std::optional<int > const sourceIndex = toInt (match[ 1 ]. str () );
228- std::optional<int > const start = toInt (match[ 2 ]. str () );
229- std::optional<int > const end = toInt (match[ 3 ]. str () );
254+ std::optional<int > const sourceIndex = toInt (*rawSourceIndex );
255+ std::optional<int > const start = toInt (*rawStart );
256+ std::optional<int > const end = toInt (*rawEnd );
230257
231- if (!sourceIndex.has_value () || !start.has_value () || !end.has_value ())
258+ if (
259+ !sourceIndex.has_value () || *sourceIndex < -1 ||
260+ !start.has_value () || *start < -1 ||
261+ !end.has_value () || *end < -1
262+ )
232263 m_errorReporter.syntaxError (
233264 6367_error,
234265 _commentLocation,
235266 " Invalid value in source location mapping. "
236267 " Expected non-negative integer values or -1 for source index and location."
237268 );
238269 else if (sourceIndex == -1 )
239- return {{tail, SourceLocation{start. value (), end. value () , nullptr }}};
240- else if (!(sourceIndex >= 0 && m_sourceNames->count (static_cast <unsigned >(sourceIndex. value () ))))
270+ return {{tail, SourceLocation{* start, * end, nullptr }}};
271+ else if (!(sourceIndex >= 0 && m_sourceNames->count (static_cast <unsigned >(* sourceIndex))))
241272 m_errorReporter.syntaxError (
242273 2674_error,
243274 _commentLocation,
244275 " Invalid source mapping. Source index not defined via @use-src."
245276 );
246277 else
247278 {
248- std::shared_ptr<std::string const > sourceName = m_sourceNames->at (static_cast <unsigned >(sourceIndex. value () ));
279+ std::shared_ptr<std::string const > sourceName = m_sourceNames->at (static_cast <unsigned >(* sourceIndex));
249280 solAssert (sourceName, " " );
250- return {{tail, SourceLocation{start. value (), end. value () , std::move (sourceName)}}};
281+ return {{tail, SourceLocation{* start, * end, std::move (sourceName)}}};
251282 }
252283 return {{tail, SourceLocation{}}};
253284}
0 commit comments