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

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