1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <fstream>
17 #include <iomanip>
18 #include <iostream>
19 #include <map>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23
24 #include "assembly-emitter.h"
25 #include "assembly-parser.h"
26 #include "error.h"
27 #include "lexer.h"
28 #include "utils/expected.h"
29 #include "utils/logger.h"
30 #include "utils/pandargs.h"
31
32 namespace panda::pandasm {
33
PrintError(const panda::pandasm::Error & e,const std::string & msg)34 void PrintError(const panda::pandasm::Error &e, const std::string &msg)
35 {
36 std::stringstream sos;
37 std::cerr << msg << ": " << e.message << std::endl;
38 sos << " Line " << e.line_number << ", Column " << e.pos + 1 << ": ";
39 std::cerr << sos.str() << e.whole_line << std::endl;
40 std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl;
41 }
42
PrintErrors(const panda::pandasm::ErrorList & warnings,const std::string & msg)43 void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &msg)
44 {
45 for (const auto &iter : warnings) {
46 PrintError(iter, msg);
47 }
48 }
49
PrepareArgs(panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & input_file,const panda::PandArg<std::string> & output_file,const panda::PandArg<std::string> & log_file,const panda::PandArg<bool> & help,const panda::PandArg<bool> & verbose,std::ifstream & inputfile,int argc,char ** argv)50 bool PrepareArgs(panda::PandArgParser &pa_parser, const panda::PandArg<std::string> &input_file,
51 const panda::PandArg<std::string> &output_file, const panda::PandArg<std::string> &log_file,
52 const panda::PandArg<bool> &help, const panda::PandArg<bool> &verbose, std::ifstream &inputfile,
53 int argc, char **argv)
54 {
55 if (!pa_parser.Parse(argc, const_cast<const char **>(argv)) || input_file.GetValue().empty() ||
56 output_file.GetValue().empty() || help.GetValue()) {
57 std::cerr << "Usage:" << std::endl;
58 std::cerr << "ark_asm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl;
59 std::cerr << "Supported options:" << std::endl << std::endl;
60 std::cerr << pa_parser.GetHelpString() << std::endl;
61 return false;
62 }
63
64 if (verbose.GetValue()) {
65 if (log_file.GetValue().empty()) {
66 panda::Logger::ComponentMask component_mask;
67 component_mask.set(panda::Logger::Component::ASSEMBLER);
68 panda::Logger::InitializeStdLogging(panda::Logger::Level::DEBUG, component_mask);
69 } else {
70 panda::Logger::ComponentMask component_mask;
71 component_mask.set(panda::Logger::Component::ASSEMBLER);
72 panda::Logger::InitializeFileLogging(log_file.GetValue(), panda::Logger::Level::DEBUG, component_mask);
73 }
74 }
75
76 inputfile.open(input_file.GetValue(), std::ifstream::in);
77
78 if (!inputfile) {
79 std::cerr << "The input file does not exist." << std::endl;
80 return false;
81 }
82
83 return true;
84 }
85
Tokenize(panda::pandasm::Lexer & lexer,std::vector<std::vector<panda::pandasm::Token>> & tokens,std::ifstream & inputfile)86 bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::pandasm::Token>> &tokens,
87 std::ifstream &inputfile)
88 {
89 std::string s;
90
91 while (getline(inputfile, s)) {
92 panda::pandasm::Tokens q = lexer.TokenizeString(s);
93
94 auto e = q.second;
95 if (e.err != panda::pandasm::Error::ErrorType::ERR_NONE) {
96 e.line_number = tokens.size() + 1;
97 PrintError(e, "ERROR");
98 return false;
99 }
100
101 tokens.push_back(q.first);
102 }
103
104 return true;
105 }
106
ParseProgram(panda::pandasm::Parser & parser,std::vector<std::vector<panda::pandasm::Token>> & tokens,const panda::PandArg<std::string> & input_file,panda::Expected<panda::pandasm::Program,panda::pandasm::Error> & res)107 bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda::pandasm::Token>> &tokens,
108 const panda::PandArg<std::string> &input_file,
109 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res)
110 {
111 res = parser.Parse(tokens, input_file.GetValue());
112 if (!res) {
113 PrintError(res.Error(), "ERROR");
114 return false;
115 }
116
117 return true;
118 }
119
DumpProgramInJson(panda::pandasm::Program & program,const panda::PandArg<std::string> & scopes_file)120 bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<std::string> &scopes_file)
121 {
122 if (!scopes_file.GetValue().empty()) {
123 std::ofstream dump_file;
124 dump_file.open(scopes_file.GetValue());
125
126 if (!dump_file) {
127 std::cerr << "Failed to write scopes into the given file." << std::endl;
128 return false;
129 }
130 dump_file << program.JsonDump();
131 }
132
133 return true;
134 }
135
EmitProgramInBinary(panda::pandasm::Program & program,panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & output_file,panda::PandArg<bool> & optimize,const panda::PandArg<bool> & size_stat)136 bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
137 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
138 const panda::PandArg<bool> &size_stat)
139 {
140 auto emit_debug_info = !optimize.GetValue();
141 std::map<std::string, size_t> stat;
142 std::map<std::string, size_t> *statp = size_stat.GetValue() ? &stat : nullptr;
143 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
144 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr;
145
146 if (!panda::pandasm::AsmEmitter::Emit(output_file.GetValue(), program, statp, mapsp, emit_debug_info)) {
147 std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl;
148 return false;
149 }
150
151 if (size_stat.GetValue()) {
152 size_t total_size = 0;
153 std::cout << "Panda file size statistic:" << std::endl;
154
155 for (auto [name, size] : stat) {
156 std::cout << name << " section: " << size << std::endl;
157 total_size += size;
158 }
159
160 std::cout << "total: " << total_size << std::endl;
161 }
162
163 pa_parser.DisableTail();
164
165 return true;
166 }
167
BuildFiles(panda::pandasm::Program & program,panda::PandArgParser & pa_parser,const panda::PandArg<std::string> & output_file,panda::PandArg<bool> & optimize,panda::PandArg<bool> & size_stat,panda::PandArg<std::string> & scopes_file)168 bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parser,
169 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize,
170 panda::PandArg<bool> &size_stat, panda::PandArg<std::string> &scopes_file)
171 {
172 if (!DumpProgramInJson(program, scopes_file)) {
173 return false;
174 }
175
176 if (!EmitProgramInBinary(program, pa_parser, output_file, optimize, size_stat)) {
177 return false;
178 }
179
180 return true;
181 }
182
183 } // namespace panda::pandasm
184
main(int argc,char * argv[])185 int main(int argc, char *argv[])
186 {
187 panda::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)");
188 panda::PandArg<std::string> log_file("log-file", "", "(--log-file FILENAME) Set log file name");
189 panda::PandArg<std::string> scopes_file("dump-scopes", "",
190 "(--dump-scopes FILENAME) Enable dump of scopes to file");
191 panda::PandArg<bool> help("help", false, "Print this message and exit");
192 panda::PandArg<bool> size_stat("size-stat", false, "Print panda file size statistic");
193 panda::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization");
194 // tail arguments
195 panda::PandArg<std::string> input_file("INPUT_FILE", "", "Path to the source assembly code");
196 panda::PandArg<std::string> output_file("OUTPUT_FILE", "", "Path to the generated binary code");
197 panda::PandArgParser pa_parser;
198 pa_parser.Add(&verbose);
199 pa_parser.Add(&help);
200 pa_parser.Add(&log_file);
201 pa_parser.Add(&scopes_file);
202 pa_parser.Add(&size_stat);
203 pa_parser.Add(&optimize);
204 pa_parser.PushBackTail(&input_file);
205 pa_parser.PushBackTail(&output_file);
206 pa_parser.EnableTail();
207
208 std::ifstream inputfile;
209
210 if (!panda::pandasm::PrepareArgs(pa_parser, input_file, output_file, log_file, help, verbose, inputfile, argc,
211 argv)) {
212 return 1;
213 }
214
215 LOG(DEBUG, ASSEMBLER) << "Lexical analysis:";
216
217 panda::pandasm::Lexer lexer;
218
219 std::vector<std::vector<panda::pandasm::Token>> tokens;
220
221 if (!Tokenize(lexer, tokens, inputfile)) {
222 return 1;
223 }
224
225 LOG(DEBUG, ASSEMBLER) << "parsing:";
226
227 panda::pandasm::Parser parser;
228
229 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> res;
230 if (!panda::pandasm::ParseProgram(parser, tokens, input_file, res)) {
231 return 1;
232 }
233
234 auto &program = res.Value();
235
236 auto w = parser.ShowWarnings();
237 if (!w.empty()) {
238 panda::pandasm::PrintErrors(w, "WARNING");
239 }
240
241 if (!panda::pandasm::BuildFiles(program, pa_parser, output_file, optimize, size_stat, scopes_file)) {
242 return 1;
243 }
244
245 return 0;
246 }
247