28
28
29
29
#include < fmt/format.h>
30
30
31
- #include < sstream>
32
-
33
31
using namespace std ::string_literals;
34
32
using namespace solidity ;
35
33
using namespace solidity ::test;
@@ -39,16 +37,46 @@ using namespace solidity::langutil;
39
37
40
38
Json PlainAssemblyParser::parse (std::string _sourceName, std::string const & _source)
41
39
{
40
+ m_sourceStream = std::istringstream (_source);
42
41
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 ())
46
54
{
47
- advanceLine (m_line);
48
55
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 );
49
74
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." )));
50
78
51
- if (c_instructions.contains (currentToken ().value ))
79
+ if (c_instructions.contains (currentToken ().value ) || currentToken (). value == " PUSHSIZE " )
52
80
{
53
81
expectNoMoreArguments ();
54
82
codeJSON.push_back ({{" name" , currentToken ().value }});
@@ -62,6 +90,19 @@ Json PlainAssemblyParser::parse(std::string _sourceName, std::string const& _sou
62
90
expectNoMoreArguments ();
63
91
codeJSON.push_back ({{" name" , " PUSH [tag]" }, {" value" , tagID}});
64
92
}
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
+ }
65
106
else
66
107
{
67
108
std::string_view immediateArgument = expectArgument ();
@@ -84,8 +125,24 @@ Json PlainAssemblyParser::parse(std::string _sourceName, std::string const& _sou
84
125
}
85
126
else
86
127
BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Unknown instruction." )));
128
+
129
+ advanceLine ();
87
130
}
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 ;
89
146
}
90
147
91
148
PlainAssemblyParser::Token const & PlainAssemblyParser::currentToken () const
@@ -100,6 +157,16 @@ PlainAssemblyParser::Token const& PlainAssemblyParser::nextToken() const
100
157
return m_lineTokens[m_tokenIndex + 1 ];
101
158
}
102
159
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
+
103
170
bool PlainAssemblyParser::advanceToken ()
104
171
{
105
172
if (!hasMoreTokens ())
@@ -125,12 +192,20 @@ void PlainAssemblyParser::expectNoMoreArguments()
125
192
BOOST_THROW_EXCEPTION (std::runtime_error (formatError (" Too many arguments." )));
126
193
}
127
194
128
- void PlainAssemblyParser::advanceLine (std::string_view _line )
195
+ bool PlainAssemblyParser::advanceLine ()
129
196
{
197
+ std::string line;
198
+ if (!getline (m_sourceStream, line))
199
+ {
200
+ m_line = std::nullopt;
201
+ return false ;
202
+ }
203
+
130
204
++m_lineNumber;
131
- m_line = _line ;
132
- m_lineTokens = tokenizeLine (m_line);
205
+ m_line = std::move (line) ;
206
+ m_lineTokens = tokenizeLine (* m_line);
133
207
m_tokenIndex = 0 ;
208
+ return true ;
134
209
}
135
210
136
211
std::vector<PlainAssemblyParser::Token> PlainAssemblyParser::tokenizeLine (std::string_view _line)
@@ -162,6 +237,9 @@ std::vector<PlainAssemblyParser::Token> PlainAssemblyParser::tokenizeLine(std::s
162
237
163
238
std::string PlainAssemblyParser::formatError (std::string_view _message) const
164
239
{
240
+ soltestAssert (m_line.has_value ());
241
+ soltestAssert (!m_lineTokens.empty ());
242
+
165
243
std::string lineNumberString = std::to_string (m_lineNumber);
166
244
std::string padding (lineNumberString.size (), ' ' );
167
245
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
174
252
_message,
175
253
padding, m_sourceName,
176
254
padding,
177
- m_lineNumber, m_line,
255
+ m_lineNumber, * m_line,
178
256
padding, underline
179
257
);
180
258
}
0 commit comments