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.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. void BXParser::skip(char expected) {
  16. int ch = get();
  17. if (ch == EOF) {
  18. error(std::string("Expected '") + expected + "', got EOF");
  19. } else if (ch != expected) {
  20. error(std::string("Expected '") + expected + "', got '" + (char)ch + "'");
  21. }
  22. }
  23. [[noreturn]] void BXParser::error(std::string msg) {
  24. throw BXParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg);
  25. }
  26. [[noreturn]] void BXParser::error(std::string msg, TokenKind kind) {
  27. switch (kind) {
  28. case TokenKind::E_O_F:
  29. msg += " EOF";
  30. break;
  31. case TokenKind::INDENTATION:
  32. msg += " indentation";
  33. break;
  34. case TokenKind::NEWLINE:
  35. msg += " newline";
  36. break;
  37. case TokenKind::COMMA:
  38. msg += " comma ','";
  39. break;
  40. case TokenKind::COLON_EQUALS:
  41. msg += " colon equals ':='";
  42. break;
  43. case TokenKind::PLUS_EQUALS:
  44. msg += " plus equals '+='";
  45. break;
  46. case TokenKind::EQUALS_PLUS:
  47. msg += " equals plus '=+'";
  48. break;
  49. case TokenKind::BAR_EQUALS:
  50. msg += " bar equals '|='";
  51. break;
  52. case TokenKind::EXPANSION:
  53. msg += " expansion";
  54. break;
  55. case TokenKind::STRING:
  56. msg += " string";
  57. break;
  58. case TokenKind::NONE:
  59. msg += " none";
  60. break;
  61. }
  62. error(msg);
  63. }
  64. std::string BXParser::readIdent(const BXVariables &vars) {
  65. std::string str;
  66. int ch;
  67. while ((ch = peek()) != EOF) {
  68. if (
  69. (ch >= 'a' && ch <= 'z') ||
  70. (ch >= 'A' && ch <= 'Z') ||
  71. (ch == '_')) {
  72. str.push_back(ch);
  73. get();
  74. } else {
  75. break;
  76. }
  77. }
  78. return str;
  79. }
  80. void BXParser::skipWhitespace() {
  81. int ch;
  82. while ((ch = peek()) == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
  83. get();
  84. }
  85. }
  86. char BXParser::readEscape() {
  87. int ch = get();
  88. if (ch == EOF) {
  89. error("Unexpected EOF");
  90. } else if (ch == 'n') {
  91. return '\n';
  92. } else if (ch == 'r') {
  93. return '\r';
  94. } else if (ch == 't') {
  95. return '\t';
  96. } else {
  97. return (char)ch;
  98. }
  99. }
  100. std::string BXParser::readStringExpansion(const BXVariables &vars) {
  101. bool braced = peek() == '{';
  102. std::string key;
  103. if (braced) {
  104. get();
  105. skipWhitespace();
  106. key = readString(vars);
  107. } else {
  108. key = readIdent(vars);
  109. }
  110. auto it = vars.find(key);
  111. if (it == vars.end()) {
  112. error("Key '" + key + "' doesn't exist");
  113. }
  114. if (braced) {
  115. skipWhitespace();
  116. if (peek() != '}') {
  117. error("Expected a '}' after a '${' expansion");
  118. }
  119. get();
  120. }
  121. // TODO: Use BXValue.asString()
  122. return it->second[0];
  123. }
  124. std::string BXParser::readQuotedString(const BXVariables &vars) {
  125. std::string str;
  126. int ch;
  127. while ((ch = peek()) != EOF) {
  128. if (ch == '\\') {
  129. get();
  130. str.push_back(readEscape());
  131. } else if (ch == '$') {
  132. get();
  133. str += readStringExpansion(vars);
  134. } else if (ch == '"') {
  135. get();
  136. break;
  137. } else {
  138. str.push_back(ch);
  139. get();
  140. }
  141. }
  142. return str;
  143. }
  144. std::string BXParser::readString(const BXVariables &vars) {
  145. std::string str;
  146. int ch;
  147. while ((ch = peek()) != EOF) {
  148. if (ch == '\\') {
  149. get();
  150. str.push_back(readEscape());
  151. } else if (ch == '$') {
  152. get();
  153. str += readStringExpansion(vars);
  154. } else if (ch == '"') {
  155. get();
  156. str += readQuotedString(vars);
  157. } else if (
  158. ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ||
  159. ch == '}' || ch == ',') {
  160. break;
  161. } else {
  162. str.push_back(ch);
  163. get();
  164. }
  165. }
  166. return str;
  167. }
  168. BXParser::Token BXParser::getToken(const BXVariables &vars) {
  169. Token tok;
  170. tok.line = line();
  171. tok.ch = ch();
  172. int ch = peek();
  173. if (ch == EOF) {
  174. tok.kind = TokenKind::E_O_F;
  175. return tok;
  176. }
  177. if (ch == '\t' || ch == ' ') {
  178. tok.kind = TokenKind::INDENTATION;
  179. do {
  180. get();
  181. ch = peek();
  182. } while (ch == '\t' || ch == ' ');
  183. return tok;
  184. } else if (ch == '\n' || ch == '\r') {
  185. tok.kind = TokenKind::NEWLINE;
  186. do {
  187. get();
  188. ch = peek();
  189. } while (ch == '\n' || ch == '\r');
  190. return tok;
  191. }
  192. int ch2 = peek(2);
  193. if (ch == ',') {
  194. get();
  195. tok.kind = TokenKind::COMMA;
  196. } else if (ch == ':' && ch2 == '=') {
  197. get(); get();
  198. tok.kind = TokenKind::COLON_EQUALS;
  199. } else if (ch == '+' && ch2 == '=') {
  200. get(); get();
  201. tok.kind = TokenKind::PLUS_EQUALS;
  202. } else if (ch == '=' && ch2 == '+') {
  203. get(); get();
  204. tok.kind = TokenKind::EQUALS_PLUS;
  205. } else if (ch == '|' && ch2 == '=') {
  206. get(); get();
  207. tok.kind = TokenKind::BAR_EQUALS;
  208. } else if (ch == '$' && ch2 == '{') {
  209. get(); get();
  210. skipWhitespace();
  211. tok.kind = TokenKind::EXPANSION;
  212. tok.str = readString(vars);
  213. skipWhitespace();
  214. if (peek() != '}') {
  215. error("Expected a '}' after a '${' expansion.");
  216. }
  217. get();
  218. } else if (ch == '$') {
  219. get();
  220. tok.kind = TokenKind::EXPANSION;
  221. tok.str = readString(vars);
  222. } else {
  223. tok.kind = TokenKind::STRING;
  224. tok.str = readString(vars);
  225. }
  226. while ((ch = peek()) == '\t' || ch == ' ') {
  227. get();
  228. }
  229. return tok;
  230. }
  231. BXParser::Token BXParser::readToken(const BXVariables &vars) {
  232. Token t = tok_;
  233. tok_ = getToken(vars);
  234. return t;
  235. }
  236. void BXWriter::escape(const std::string &str) {
  237. buf_.put('"');
  238. for (char ch: str) {
  239. if (ch == '$' || ch == '"' || ch == '\\') {
  240. buf_.put('\\');
  241. }
  242. buf_.put(ch);
  243. }
  244. buf_.put('"');
  245. }
  246. void BXWriter::write(const BXVariables &vars) {
  247. for (const auto &pair: vars) {
  248. size_t chars = 0;
  249. buf_.put(pair.first);
  250. buf_.put(" :=");
  251. for (auto &val: pair.second) {
  252. if (chars >= 80) {
  253. buf_.put('\n');
  254. buf_.put('\t');
  255. chars = 0;
  256. } else {
  257. buf_.put(' ');
  258. }
  259. escape(val);
  260. chars += val.size();
  261. }
  262. buf_.put('\n');
  263. }
  264. }
  265. void BXParser::parse(BXVariables &vars, bool oneLine) {
  266. readToken(vars);
  267. while (true) {
  268. if (peekToken().kind == TokenKind::E_O_F) {
  269. break;
  270. } else if (peekToken().kind != TokenKind::STRING) {
  271. error("Expected string, got", peekToken().kind);
  272. }
  273. Token t = readToken(vars);
  274. std::string key = t.str;
  275. std::vector<std::string> &var = vars[key];
  276. void (*addVal)(std::vector<std::string> &var, std::string val);
  277. switch (peekToken().kind) {
  278. case TokenKind::COLON_EQUALS:
  279. var.clear();
  280. // Fallthrough
  281. case TokenKind::PLUS_EQUALS:
  282. addVal = [](auto &var, auto val) {
  283. var.push_back(std::move(val));
  284. };
  285. break;
  286. case TokenKind::EQUALS_PLUS:
  287. addVal = [](auto &var, auto val) {
  288. var.insert(var.begin(), std::move(val));
  289. };
  290. break;
  291. case TokenKind::BAR_EQUALS:
  292. addVal = [](auto &var, auto val) {
  293. for (auto &v: var) {
  294. if (v == val) {
  295. return;
  296. }
  297. }
  298. var.push_back(val);
  299. };
  300. break;
  301. default:
  302. error("Expected operator, got", peekToken().kind);
  303. }
  304. readToken(vars);
  305. parseList(vars, var, addVal, oneLine);
  306. }
  307. }
  308. void BXParser::parseList(
  309. BXVariables &vars, std::vector<std::string> &var,
  310. void (*addVal)(std::vector<std::string> &var, std::string val),
  311. bool oneLine) {
  312. while (true) {
  313. Token tok = peekToken();
  314. switch (tok.kind) {
  315. case TokenKind::NEWLINE:
  316. if (oneLine) {
  317. return;
  318. }
  319. readToken(vars);
  320. if (peekToken().kind != TokenKind::INDENTATION) {
  321. return;
  322. }
  323. readToken(vars); // Read indentation
  324. break;
  325. case TokenKind::STRING:
  326. addVal(var, std::move(tok.str));
  327. readToken(vars);
  328. break;
  329. case TokenKind::COMMA:
  330. readToken(vars);
  331. return;
  332. case TokenKind::E_O_F:
  333. return;
  334. case TokenKind::EXPANSION:
  335. for (auto &v: vars[tok.str]) {
  336. addVal(var, v);
  337. }
  338. readToken(vars);
  339. break;
  340. default:
  341. error("Unexpected token", tok.kind);
  342. }
  343. }
  344. }
  345. void BXParser::parseList(BXVariables &vars, std::vector<std::string> &var) {
  346. auto addVal = [](auto &var, auto val) {
  347. var.push_back(std::move(val));
  348. };
  349. readToken(vars);
  350. parseList(vars, var, addVal, false);
  351. }