123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- #include "BBBParser.h"
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
-
- int BBBParser::get() {
- int ch = stream_.get();
- if (ch == '\n') {
- line_ += 1;
- ch_ = 1;
- } else {
- ch_ += 1;
- }
-
- return ch;
- }
-
- void BBBParser::skip(char expected) {
- int ch = get();
- if (ch == EOF) {
- error(std::string("Expected '") + expected + "', got EOF");
- } else if (ch != expected) {
- error(std::string("Expected '") + expected + "', got '" + (char)ch + "'");
- }
- }
-
- void BBBParser::error(std::string msg) {
- throw BBBParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg);
- }
-
- static bool isWhitespace(int ch) {
- if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
- return true;
- return false;
- }
-
- void BBBParser::skipWhitespaceLine() {
- char ch;
- while (isWhitespace(ch = peek()) && ch != '\r' && ch != '\n')
- get();
- }
-
- void BBBParser::skipWhitespace() {
- while (isWhitespace(peek()))
- get();
- }
-
- char BBBParser::parseEscape() {
- skip(); // '\'
- int ch;
- switch (ch = get()) {
- case EOF:
- error("Unexpected EOF");
-
- case 'n':
- return '\n';
-
- case 'r':
- return '\r';
-
- case 't':
- return '\t';
-
- default:
- return (char)ch;
- }
- }
-
- void BBBParser::parseExpansion(const Variables &vars, std::vector<std::string> &values) {
- skip(); // '$'
-
- auto appendVar = [&](const std::string &name) {
- if (name.size() == 0)
- return;
-
- auto it = vars.find(name);
- if (it == vars.end())
- return;
-
- auto &vec = it->second;
- for (auto &part: vec) {
- values.push_back(part);
- }
- };
-
- std::string str;
- switch (peek()) {
- case '{':
- skip();
- parseString(vars, str, '}');
- skip('}');
- appendVar(str);
- break;
-
- default:
- if (!parseString(vars, str)) {
- error("No identifier after $. Did you mean '\\$'?");
- }
-
- appendVar(str);
- }
- }
-
- void BBBParser::parseQuotedExpansion(const Variables &vars, std::string &content) {
- skip(); // '$'
-
- auto appendVar = [&](const std::string &name) {
- if (name.size() == 0)
- return;
-
- auto it = vars.find(name);
- if (it == vars.end())
- return;
-
- auto &vec = it->second;
- bool first = true;
- for (auto &part: vec) {
- if (!first) {
- content += ' ';
- }
-
- first = false;
- content += part;
- }
- };
-
- std::string str;
- switch (peek()) {
- case '{':
- skip();
- parseString(vars, str, '}');
- skip('}');
- appendVar(str);
- break;
-
- default:
- if (!parseString(vars, str, '"')) {
- error("No identifier after $. Did you mean '\\$'?");
- }
-
- appendVar(str);
- }
- }
-
- void BBBParser::parseQuotedString(const Variables &vars, std::string &content) {
- skip(); // '"'
-
- int ch;
- while ((ch = peek()) != EOF) {
- switch (ch) {
- case EOF:
- error("Unexpected EOF");
-
- case '\\':
- content.push_back(parseEscape());
- break;
-
- case '$':
- parseQuotedExpansion(vars, content);
- break;
-
- case '"':
- skip();
- return;
-
- default:
- content.push_back(get());
- break;
- }
- }
- }
-
- bool BBBParser::parseString(const Variables &vars, std::string &content, int sep) {
- bool success = false;
- int ch;
- while (1) {
- ch = peek();
- if ((sep > 0 && ch == sep) || isWhitespace(ch)) {
- return success;
- }
-
- switch (ch) {
- case '=':
- case EOF:
- return success;
-
- case '\\':
- content.push_back(parseEscape());
- break;
-
- case '"':
- parseQuotedString(vars, content);
- success = true;
- break;
-
- default:
- content.push_back(get());
- success = true;
- break;
- }
- }
- }
-
- void BBBParser::parseLine(const Variables &vars, std::vector<std::string> &values) {
- std::string value;
- while (true) {
- skipWhitespaceLine();
- if (peek() == EOF) {
- break;
- } else if (peek() == '$') {
- parseExpansion(vars, values);
- continue;
- }
-
- if (!parseString(vars, value)) {
- break;
- }
-
- values.push_back(std::move(value));
- value.clear();
- }
- }
-
- void BBBParser::parse(Variables &vars) {
- std::string key, value;
- std::vector<std::string> values;
-
- skipWhitespace();
- if (!parseString(vars, key)) {
- return;
- }
-
- skipWhitespace();
- skip('=');
-
- while (true) {
- skipWhitespace();
-
- if (peek() == EOF) {
- break;
- } else if (peek() == '$') {
- parseExpansion(vars, values);
- continue;
- }
-
- if (!parseString(vars, value)) {
- break;
- }
-
- skipWhitespace();
- if (peek() == '=') {
- if (key.size() == 0) {
- error("No variable on the left hand side of '='");
- }
-
- skip();
- vars[key] = std::move(values);
- key = std::move(value);
- value.clear();
- } else {
- values.push_back(std::move(value));
- value.clear();
- }
- }
-
- vars[key] = std::move(values);
- }
|