2828
2929#include < fmt/format.h>
3030
31- #include < sstream>
32-
3331using namespace std ::string_literals;
3432using namespace solidity ;
3533using namespace solidity ::test;
@@ -39,16 +37,46 @@ using namespace solidity::langutil;
3937
4038Json PlainAssemblyParser::parse (std::string _sourceName, std::string const & _source)
4139{
40+ m_sourceStream = std::istringstream (_source);
4241 m_sourceName = std::move (_sourceName);
43- Json codeJSON = Json::array ();
44- std::istringstream sourceStream (_source);
45- while (getline (sourceStream, m_line))
42+ m_lineNumber = 0 ;
43+
44+ advanceLine ();
45+ return parseAssembly (0 );
46+ }
47+
48+ Json PlainAssemblyParser::parseAssembly (size_t _nestingLevel)
49+ {
50+ Json assemblyJSON = {{" .code" , Json::array ()}};
51+ Json& codeJSON = assemblyJSON[" .code" ];
52+
53+ while (m_line.has_value ())
4654 {
47- advanceLine (m_line);
4855 if (m_lineTokens.empty ())
56+ {
57+ advanceLine ();
58+ continue ;
59+ }
60+
61+ size_t newLevel = parseNestingLevel ();
62+ if (newLevel > _nestingLevel)
63+ BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Indentation does not match the current subassembly nesting level." )));
64+
65+ if (newLevel < _nestingLevel)
66+ return assemblyJSON;
67+
68+ if (currentToken ().value == " .sub" )
69+ {
70+ advanceLine ();
71+
72+ std::string nextDataIndex = std::to_string (assemblyJSON[" .data" ].size ());
73+ assemblyJSON[" .data" ][nextDataIndex] = parseAssembly (_nestingLevel + 1 );
4974 continue ;
75+ }
76+ else if (assemblyJSON.contains (" .data" ))
77+ BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" The code of an assembly must be specified before its subassemblies." )));
5078
51- if (c_instructions.contains (currentToken ().value ))
79+ if (c_instructions.contains (currentToken ().value ) || currentToken (). value == " PUSHSIZE " )
5280 {
5381 expectNoMoreArguments ();
5482 codeJSON.push_back ({{" name" , currentToken ().value }});
@@ -62,6 +90,19 @@ Json PlainAssemblyParser::parse(std::string _sourceName, std::string const& _sou
6290 expectNoMoreArguments ();
6391 codeJSON.push_back ({{" name" , " PUSH [tag]" }, {" value" , tagID}});
6492 }
93+ else if (hasMoreTokens () && (nextToken ().value == " [$]" || nextToken ().value == " #[$]" ))
94+ {
95+ std::string pushType = std::string (nextToken ().value );
96+ advanceToken ();
97+ std::string_view subassemblyID = expectArgument ();
98+ expectNoMoreArguments ();
99+
100+ if (!subassemblyID.starts_with (" 0x" ))
101+ BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" The subassembly ID must be a hex number prefixed with '0x'." )));
102+
103+ subassemblyID.remove_prefix (" 0x" s.size ());
104+ codeJSON.push_back ({{" name" , " PUSH " + pushType}, {" value" , subassemblyID}});
105+ }
65106 else
66107 {
67108 std::string_view immediateArgument = expectArgument ();
@@ -84,8 +125,24 @@ Json PlainAssemblyParser::parse(std::string _sourceName, std::string const& _sou
84125 }
85126 else
86127 BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Unknown instruction." )));
128+
129+ advanceLine ();
87130 }
88- return {{" .code" , codeJSON}};
131+
132+ return assemblyJSON;
133+ }
134+
135+ size_t PlainAssemblyParser::parseNestingLevel () const
136+ {
137+ std::string_view indentationString = indentation ();
138+
139+ if (indentationString != std::string (indentationString.size (), ' ' ))
140+ BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Non-space characters used for indentation." )));
141+
142+ if (indentationString.size () % 4 != 0 )
143+ BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Each indentation level must consist of 4 spaces." )));
144+
145+ return indentationString.size () / 4 ;
89146}
90147
91148PlainAssemblyParser::Token const & PlainAssemblyParser::currentToken () const
@@ -100,6 +157,16 @@ PlainAssemblyParser::Token const& PlainAssemblyParser::nextToken() const
100157 return m_lineTokens[m_tokenIndex + 1 ];
101158}
102159
160+ std::string_view PlainAssemblyParser::indentation () const
161+ {
162+ soltestAssert (m_line.has_value ());
163+
164+ if (m_lineTokens.empty ())
165+ return *m_line;
166+
167+ return std::string_view (*m_line).substr (0 , m_lineTokens.at (0 ).position );
168+ }
169+
103170bool PlainAssemblyParser::advanceToken ()
104171{
105172 if (!hasMoreTokens ())
@@ -125,12 +192,20 @@ void PlainAssemblyParser::expectNoMoreArguments()
125192 BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Too many arguments." )));
126193}
127194
128- void PlainAssemblyParser::advanceLine (std::string_view _line )
195+ bool PlainAssemblyParser::advanceLine ()
129196{
197+ std::string line;
198+ if (!getline (m_sourceStream, line))
199+ {
200+ m_line = std::nullopt ;
201+ return false ;
202+ }
203+
130204 ++m_lineNumber;
131- m_line = _line ;
132- m_lineTokens = tokenizeLine (m_line);
205+ m_line = std::move (line) ;
206+ m_lineTokens = tokenizeLine (* m_line);
133207 m_tokenIndex = 0 ;
208+ return true ;
134209}
135210
136211std::vector<PlainAssemblyParser::Token> PlainAssemblyParser::tokenizeLine (std::string_view _line)
@@ -162,6 +237,9 @@ std::vector<PlainAssemblyParser::Token> PlainAssemblyParser::tokenizeLine(std::s
162237
163238std::string PlainAssemblyParser::formatError (std::string_view _message) const
164239{
240+ soltestAssert (m_line.has_value ());
241+ soltestAssert (!m_lineTokens.empty ());
242+
165243 std::string lineNumberString = std::to_string (m_lineNumber);
166244 std::string padding (lineNumberString.size (), ' ' );
167245 std::string underline = std::string (currentToken ().position , ' ' ) + std::string (currentToken ().value .size (), ' ^' );
@@ -174,7 +252,7 @@ std::string PlainAssemblyParser::formatError(std::string_view _message) const
174252 _message,
175253 padding, m_sourceName,
176254 padding,
177- m_lineNumber, m_line,
255+ m_lineNumber, * m_line,
178256 padding, underline
179257 );
180258}
0 commit comments