Build tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BXParser.cc 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include "BXParser.h"
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. int BXParser::get() {
  7. int ch = stream_.get();
  8. if (ch == '\n') {
  9. line_ += 1;
  10. ch_ = 1;
  11. } else {
  12. ch_ += 1;
  13. }
  14. return ch;
  15. }
  16. BXParser::Operator BXParser::readOperator() {
  17. int ch2 = peek2();
  18. if (peek() == ':' && ch2 == '=') {
  19. skip(); // ':'
  20. skip(); // '='
  21. return Operator::COLON_EQUALS;
  22. } else if (peek() == '+' && ch2 == '=') {
  23. skip(); // '+'
  24. skip(); // '='
  25. return Operator::PLUS_EQUALS;
  26. } else if (peek() == '=' && ch2 == '+') {
  27. skip(); // '='
  28. skip(); // '+'
  29. return Operator::EQUALS_PLUS;
  30. }
  31. return Operator::NONE;
  32. }
  33. void BXParser::skip(char expected) {
  34. int ch = get();
  35. if (ch == EOF) {
  36. error(std::string("Expected '") + expected + "', got EOF");
  37. } else if (ch != expected) {
  38. error(std::string("Expected '") + expected + "', got '" + (char)ch + "'");
  39. }
  40. }
  41. [[noreturn]] void BXParser::error(std::string msg) {
  42. throw BXParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg);
  43. }
  44. static bool isWhitespace(int ch) {
  45. if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
  46. return true;
  47. return false;
  48. }
  49. void BXParser::skipWhitespace() {
  50. if (flags_ & FLAG_ONE_LINE) {
  51. int ch;
  52. while (isWhitespace(ch = peek()) && ch != '\r' && ch != '\n')
  53. get();
  54. } else {
  55. while (isWhitespace(peek()))
  56. get();
  57. }
  58. }
  59. char BXParser::parseEscape() {
  60. skip(); // '\'
  61. int ch;
  62. switch (ch = get()) {
  63. case EOF:
  64. error("Unexpected EOF");
  65. case 'n':
  66. return '\n';
  67. case 'r':
  68. return '\r';
  69. case 't':
  70. return '\t';
  71. default:
  72. return (char)ch;
  73. }
  74. }
  75. static void appendVariableToString(
  76. const BXVariables &vars, std::string &name,
  77. std::string &value) {
  78. if (name.size() == 0)
  79. return;
  80. auto it = vars.find(name);
  81. if (it == vars.end())
  82. return;
  83. auto &vec = it->second;
  84. bool first = true;
  85. for (auto &part: vec) {
  86. if (!first) {
  87. value += ' ';
  88. }
  89. first = false;
  90. value += part;
  91. }
  92. }
  93. static void appendVariableToArray(
  94. const BXVariables &vars, const std::string &name,
  95. std::vector<std::string> &values) {
  96. if (name.size() == 0)
  97. return;
  98. auto it = vars.find(name);
  99. if (it == vars.end())
  100. return;
  101. auto &vec = it->second;
  102. for (auto &part: vec) {
  103. values.push_back(part);
  104. }
  105. }
  106. void BXParser::parseExpansion(const BXVariables &vars, std::vector<std::string> &values) {
  107. skip(); // '$'
  108. std::string str;
  109. switch (peek()) {
  110. case '{':
  111. skip();
  112. parseString(vars, str, '}');
  113. skip('}');
  114. appendVariableToArray(vars, str, values);
  115. break;
  116. default:
  117. if (!parseIdentifier(str)) {
  118. error("No identifier after $.");
  119. }
  120. appendVariableToArray(vars, str, values);
  121. break;
  122. }
  123. }
  124. void BXParser::parseQuotedExpansion(const BXVariables &vars, std::string &content) {
  125. skip(); // '$'
  126. std::string str;
  127. switch (peek()) {
  128. case '{':
  129. skip();
  130. parseString(vars, str, '}');
  131. skip('}');
  132. appendVariableToString(vars, str, content);
  133. break;
  134. default:
  135. if (!parseIdentifier(str)) {
  136. error("No identifier after $.");
  137. }
  138. appendVariableToString(vars, str, content);
  139. break;
  140. }
  141. }
  142. void BXParser::parseQuotedString(const BXVariables &vars, std::string &content) {
  143. skip(); // '"'
  144. int ch;
  145. while ((ch = peek()) != EOF) {
  146. switch (ch) {
  147. case EOF:
  148. error("Unexpected EOF");
  149. case '\\':
  150. content.push_back(parseEscape());
  151. break;
  152. case '$':
  153. parseQuotedExpansion(vars, content);
  154. break;
  155. case '"':
  156. skip();
  157. return;
  158. default:
  159. content.push_back(get());
  160. break;
  161. }
  162. }
  163. }
  164. bool BXParser::parseString(const BXVariables &vars, std::string &content, int sep) {
  165. bool success = false;
  166. int ch;
  167. while (1) {
  168. ch = peek();
  169. if ((sep > 0 && ch == sep) || isWhitespace(ch)) {
  170. return success;
  171. }
  172. switch (ch) {
  173. case EOF:
  174. return success;
  175. case '\\':
  176. content.push_back(parseEscape());
  177. success = true;
  178. break;
  179. case '$':
  180. parseQuotedExpansion(vars, content);
  181. success = true;
  182. break;
  183. case '"':
  184. parseQuotedString(vars, content);
  185. success = true;
  186. break;
  187. default:
  188. if (ch == ':' && peek2() == '=')
  189. return success;
  190. content.push_back(get());
  191. success = true;
  192. break;
  193. }
  194. }
  195. }
  196. bool BXParser::parseIdentifier(std::string &content) {
  197. int ch = peek();
  198. if (!(
  199. (ch >= 'a' && ch <= 'z') ||
  200. (ch >= 'A' && ch <= 'Z') ||
  201. (ch == '_'))) {
  202. return false;
  203. }
  204. content += get();
  205. while (1) {
  206. ch = peek();
  207. if (!(
  208. (ch >= '0' && ch <= '9') ||
  209. (ch >= 'a' && ch <= 'z') ||
  210. (ch >= 'A' && ch <= 'Z') ||
  211. (ch == '_'))) {
  212. return true;
  213. }
  214. content += get();
  215. }
  216. }
  217. void BXParser::parse(BXVariables &vars) {
  218. std::string key, value;
  219. std::vector<std::string> values;
  220. skipWhitespace();
  221. if (!parseString(vars, key)) {
  222. return;
  223. }
  224. skipWhitespace();
  225. Operator prevOper = readOperator();
  226. if (prevOper == Operator::NONE) {
  227. error("Expected operator.");
  228. }
  229. auto doAssignment = [&] {
  230. switch (prevOper) {
  231. case Operator::COLON_EQUALS:
  232. vars[key] = std::move(values);
  233. values.clear();
  234. break;
  235. case Operator::PLUS_EQUALS:
  236. {
  237. auto &vec = vars[key];
  238. vec.reserve(vec.size() + values.size());
  239. for (size_t i = 0; i < values.size(); ++i) {
  240. vec.push_back(std::move(values[i]));
  241. }
  242. }
  243. values.clear();
  244. break;
  245. case Operator::EQUALS_PLUS:
  246. {
  247. auto &vec = vars[key];
  248. vec.reserve(vec.size() + values.size());
  249. for (size_t i = 0; i < vec.size(); ++i) {
  250. values.push_back(std::move(vec[i]));
  251. }
  252. vec = std::move(values);
  253. }
  254. values.clear();
  255. break;
  256. case Operator::NONE:
  257. break;
  258. }
  259. };
  260. while (true) {
  261. skipWhitespace();
  262. // Parse next value
  263. if (peek() == '$') {
  264. parseExpansion(vars, values);
  265. value.clear();
  266. continue; // We can't have an assignment after an expansion
  267. } else if (!parseString(vars, value)) {
  268. break;
  269. }
  270. skipWhitespace();
  271. // If there's an operator next, the value we just read was a actually a key.
  272. // Otherwise, it was just another value.
  273. Operator op = readOperator();
  274. if (op == Operator::NONE) {
  275. values.push_back(std::move(value));
  276. value.clear();
  277. } else {
  278. if (value.size() == 0) {
  279. error("Expected string before assignment operator");
  280. }
  281. doAssignment();
  282. prevOper = op;
  283. key = std::move(value);
  284. value.clear();
  285. }
  286. }
  287. doAssignment();
  288. }
  289. void BXParser::parseList(const BXVariables &vars, std::vector<std::string> &values) {
  290. while (true) {
  291. skipWhitespace();
  292. std::string value;
  293. if (!parseString(vars, value)) {
  294. break;
  295. }
  296. values.push_back(std::move(value));
  297. }
  298. }
  299. void BXWriter::put(char ch) {
  300. ch_ += 1;
  301. stream_ << ch;
  302. }
  303. void BXWriter::put(const std::string &str) {
  304. ch_ += str.size();
  305. stream_ << str;
  306. }
  307. void BXWriter::newline() {
  308. ch_ = 1;
  309. line_ += 1;
  310. stream_ << '\n';
  311. }
  312. void BXWriter::escape(const std::string &str) {
  313. put('"');
  314. for (char ch: str) {
  315. if (ch == '$' || ch == '"' || ch == '\\') {
  316. put('\\');
  317. }
  318. put(ch);
  319. }
  320. put('"');
  321. }
  322. void BXWriter::write(const BXVariables &vars) {
  323. for (const auto &pair: vars) {
  324. put(pair.first);
  325. put(" :=");
  326. for (auto &val: pair.second) {
  327. if (ch_ >= 80) {
  328. newline();
  329. put('\t');
  330. } else {
  331. put(' ');
  332. }
  333. escape(val);
  334. }
  335. newline();
  336. }
  337. }