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.

BBBParser.cc 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #include "BBBParser.h"
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. int BBBParser::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. BBBParser::Operator BBBParser::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 BBBParser::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 BBBParser::error(std::string msg) {
  42. throw BBBParseError(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 BBBParser::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 BBBParser::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 BBBParser::Variables &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 BBBParser::Variables &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 BBBParser::parseExpansion(const Variables &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 BBBParser::parseQuotedExpansion(const Variables &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 BBBParser::parseQuotedString(const Variables &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 BBBParser::parseString(const Variables &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 BBBParser::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 BBBParser::parse(Variables &vars) {
  218. std::string key, value;
  219. std::vector<std::string> values;
  220. skipWhitespace();
  221. if (!parseString(vars, key)) {
  222. return;
  223. }
  224. Operator prevOper = readOperator();
  225. if (prevOper == Operator::NONE) {
  226. error("Expected operator.");
  227. }
  228. auto doAssignment = [&] {
  229. switch (prevOper) {
  230. case Operator::COLON_EQUALS:
  231. vars[key] = std::move(values);
  232. values.clear();
  233. break;
  234. case Operator::PLUS_EQUALS:
  235. {
  236. auto &vec = vars[key];
  237. vec.reserve(vec.size() + values.size());
  238. for (size_t i = 0; i < values.size(); ++i) {
  239. vec.push_back(std::move(values[i]));
  240. }
  241. }
  242. values.clear();
  243. break;
  244. case Operator::EQUALS_PLUS:
  245. {
  246. auto &vec = vars[key];
  247. vec.reserve(vec.size() + values.size());
  248. for (size_t i = 0; i < vec.size(); ++i) {
  249. values.push_back(std::move(vec[i]));
  250. }
  251. vec = std::move(values);
  252. }
  253. values.clear();
  254. break;
  255. case Operator::NONE:
  256. break;
  257. }
  258. };
  259. while (true) {
  260. skipWhitespace();
  261. // Parse next value
  262. if (peek() == '$') {
  263. parseExpansion(vars, values);
  264. value.clear();
  265. continue; // We can't have an assignment after an expansion
  266. } else if (!parseString(vars, value)) {
  267. break;
  268. }
  269. skipWhitespace();
  270. // If there's an operator next, the value we just read was a actually a key.
  271. // Otherwise, it was just another value.
  272. Operator op = readOperator();
  273. if (op == Operator::NONE) {
  274. values.push_back(std::move(value));
  275. value.clear();
  276. } else {
  277. if (value.size() == 0) {
  278. error("Expected string before assignment operator");
  279. }
  280. doAssignment();
  281. prevOper = op;
  282. key = std::move(value);
  283. value.clear();
  284. }
  285. }
  286. doAssignment();
  287. }