1/** 2 * Copyright (c) 2021-2022 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 "ark_version.h" 25 26#include "assembly-emitter.h" 27#include "assembly-parser.h" 28#include "file_format_version.h" 29#include "error.h" 30#include "lexer.h" 31#include "utils/expected.h" 32#include "utils/logger.h" 33#include "utils/pandargs.h" 34#include "pandasm.h" 35 36namespace panda::pandasm { 37 38void PrintError(const panda::pandasm::Error &e, const std::string &msg) 39{ 40 std::stringstream sos; 41 std::cerr << msg << ": " << e.message << std::endl; 42 sos << " Line " << e.line_number << ", Column " << e.pos + 1 << ": "; 43 std::cerr << sos.str() << e.whole_line << std::endl; 44 std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl; 45} 46 47void PrintErrors(const panda::pandasm::ErrorList &warnings, const std::string &msg) 48{ 49 for (const auto &iter : warnings) { 50 PrintError(iter, msg); 51 } 52} 53 54void PrintHelp(const panda::PandArgParser &pa_parser) 55{ 56 std::cerr << "Usage:" << std::endl; 57 std::cerr << "pandasm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl; 58 std::cerr << "Supported options:" << std::endl << std::endl; 59 std::cerr << pa_parser.GetHelpString() << std::endl; 60} 61 62bool PrepareArgs(panda::PandArgParser &pa_parser, const panda::PandArg<std::string> &input_file, 63 const panda::PandArg<std::string> &output_file, const panda::PandArg<std::string> &log_file, 64 const panda::PandArg<bool> &help, const panda::PandArg<bool> &verbose, 65 const panda::PandArg<bool> &version, std::ifstream &inputfile, int argc, const char **argv) 66{ 67 if (!pa_parser.Parse(argc, argv)) { 68 PrintHelp(pa_parser); 69 return false; 70 } 71 72 if (version.GetValue()) { 73 panda::PrintPandaVersion(); 74 panda_file::PrintBytecodeVersion(); 75 return false; 76 } 77 78 if (input_file.GetValue().empty() || output_file.GetValue().empty() || help.GetValue()) { 79 PrintHelp(pa_parser); 80 return false; 81 } 82 83 if (verbose.GetValue()) { 84 if (log_file.GetValue().empty()) { 85 panda::Logger::ComponentMask component_mask; 86 component_mask.set(panda::Logger::Component::ASSEMBLER); 87 component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER); 88 panda::Logger::InitializeStdLogging(panda::Logger::Level::DEBUG, component_mask); 89 } else { 90 panda::Logger::ComponentMask component_mask; 91 component_mask.set(panda::Logger::Component::ASSEMBLER); 92 component_mask.set(panda::Logger::Component::BYTECODE_OPTIMIZER); 93 panda::Logger::InitializeFileLogging(log_file.GetValue(), panda::Logger::Level::DEBUG, component_mask); 94 } 95 } 96 97 inputfile.open(input_file.GetValue(), std::ifstream::in); 98 99 if (!inputfile) { 100 std::cerr << "The input file does not exist." << std::endl; 101 return false; 102 } 103 104 return true; 105} 106 107bool Tokenize(panda::pandasm::Lexer &lexer, std::vector<std::vector<panda::pandasm::Token>> &tokens, 108 std::ifstream &inputfile) 109{ 110 std::string s; 111 112 while (getline(inputfile, s)) { 113 panda::pandasm::Tokens q = lexer.TokenizeString(s); 114 115 auto e = q.second; 116 117 if (e.err != panda::pandasm::Error::ErrorType::ERR_NONE) { 118 e.line_number = tokens.size() + 1; 119 PrintError(e, "ERROR"); 120 return false; 121 } 122 123 tokens.push_back(q.first); 124 } 125 126 return true; 127} 128 129bool ParseProgram(panda::pandasm::Parser &parser, std::vector<std::vector<panda::pandasm::Token>> &tokens, 130 const panda::PandArg<std::string> &input_file, 131 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> &res) 132{ 133 res = parser.Parse(tokens, input_file.GetValue()); 134 if (!res) { 135 PrintError(res.Error(), "ERROR"); 136 return false; 137 } 138 139 return true; 140} 141 142bool DumpProgramInJson(panda::pandasm::Program &program, const panda::PandArg<std::string> &scopes_file) 143{ 144 if (!scopes_file.GetValue().empty()) { 145 std::ofstream dump_file; 146 dump_file.open(scopes_file.GetValue()); 147 148 if (!dump_file) { 149 std::cerr << "Cannot write scopes into the given file." << std::endl; 150 return false; 151 } 152 dump_file << program.JsonDump(); 153 } 154 155 return true; 156} 157 158bool EmitProgramInBinary(panda::pandasm::Program &program, panda::PandArgParser &pa_parser, 159 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize, 160 panda::PandArg<bool> &size_stat) 161{ 162 auto emit_debug_info = !optimize.GetValue(); 163 std::map<std::string, size_t> stat; 164 std::map<std::string, size_t> *statp = size_stat.GetValue() ? &stat : nullptr; 165 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {}; 166 panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr; 167 168 if (!panda::pandasm::AsmEmitter::Emit(output_file.GetValue(), program, statp, mapsp, emit_debug_info)) { 169 std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl; 170 return false; 171 } 172 173 if (size_stat.GetValue()) { 174 size_t total_size = 0; 175 std::cout << "Panda file size statistic:" << std::endl; 176 177 for (auto [name, size] : stat) { 178 std::cout << name << " section: " << size << std::endl; 179 total_size += size; 180 } 181 182 std::cout << "total: " << total_size << std::endl; 183 } 184 185 pa_parser.DisableTail(); 186 187 return true; 188} 189 190bool BuildFiles(panda::pandasm::Program &program, panda::PandArgParser &pa_parser, 191 const panda::PandArg<std::string> &output_file, panda::PandArg<bool> &optimize, 192 panda::PandArg<bool> &size_stat, panda::PandArg<std::string> &scopes_file) 193{ 194 if (!DumpProgramInJson(program, scopes_file)) { 195 return false; 196 } 197 198 if (!EmitProgramInBinary(program, pa_parser, output_file, optimize, size_stat)) { 199 return false; 200 } 201 202 return true; 203} 204 205} // namespace panda::pandasm 206 207int main(int argc, const char *argv[]) 208{ 209 panda::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)"); 210 panda::PandArg<std::string> log_file("log-file", "", "(--log-file FILENAME) Set log file name"); 211 panda::PandArg<std::string> scopes_file("dump-scopes", "", 212 "(--dump-scopes FILENAME) Enable dump of scopes to file"); 213 panda::PandArg<bool> help("help", false, "Print this message and exit"); 214 panda::PandArg<bool> size_stat("size-stat", false, "Print panda file size statistic"); 215 panda::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization"); 216 panda::PandArg<bool> version {"version", false, 217 "Ark version, file format version and minimum supported file format version"}; 218 // tail arguments 219 panda::PandArg<std::string> input_file("INPUT_FILE", "", "Path to the source assembly code"); 220 panda::PandArg<std::string> output_file("OUTPUT_FILE", "", "Path to the generated binary code"); 221 panda::PandArgParser pa_parser; 222 pa_parser.Add(&verbose); 223 pa_parser.Add(&help); 224 pa_parser.Add(&log_file); 225 pa_parser.Add(&scopes_file); 226 pa_parser.Add(&size_stat); 227 pa_parser.Add(&optimize); 228 pa_parser.Add(&version); 229 pa_parser.PushBackTail(&input_file); 230 pa_parser.PushBackTail(&output_file); 231 pa_parser.EnableTail(); 232 233 std::ifstream inputfile; 234 235 if (!panda::pandasm::PrepareArgs(pa_parser, input_file, output_file, log_file, help, verbose, version, inputfile, 236 argc, argv)) { 237 return 1; 238 } 239 240 LOG(DEBUG, ASSEMBLER) << "Lexical analysis:"; 241 242 panda::pandasm::Lexer lexer; 243 244 std::vector<std::vector<panda::pandasm::Token>> tokens; 245 246 if (!Tokenize(lexer, tokens, inputfile)) { 247 return 1; 248 } 249 250 LOG(DEBUG, ASSEMBLER) << "parsing:"; 251 252 panda::pandasm::Parser parser; 253 254 panda::Expected<panda::pandasm::Program, panda::pandasm::Error> res; 255 if (!panda::pandasm::ParseProgram(parser, tokens, input_file, res)) { 256 return 1; 257 } 258 259 auto &program = res.Value(); 260 261 auto w = parser.ShowWarnings(); 262 if (!w.empty()) { 263 panda::pandasm::PrintErrors(w, "WARNING"); 264 } 265 266 if (!panda::pandasm::BuildFiles(program, pa_parser, output_file, optimize, size_stat, scopes_file)) { 267 return 1; 268 } 269 270 return 0; 271} 272