|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- #include "BXParser.h"
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
-
- int BXParser::get() {
- int c = buf_.get();
- ch_ += 1;
- if (c == '\n') {
- ch_ = 1;
- line_ += 1;
- }
-
- return c;
- }
-
- void BXParser::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 + "'");
- }
- }
-
- [[noreturn]] void BXParser::error(std::string msg) {
- throw BXParseError(std::to_string(line_) + ":" + std::to_string(ch_) + ": " + msg);
- }
-
- [[noreturn]] void BXParser::error(std::string msg, TokenKind kind) {
- switch (kind) {
- case TokenKind::E_O_F:
- msg += " EOF";
- break;
-
- case TokenKind::INDENTATION:
- msg += " indentation";
- break;
-
- case TokenKind::NEWLINE:
- msg += " newline";
- break;
-
- case TokenKind::COMMA:
- msg += " comma ','";
- break;
-
- case TokenKind::COLON_EQUALS:
- msg += " colon equals ':='";
- break;
-
- case TokenKind::PLUS_EQUALS:
- msg += " plus equals '+='";
- break;
-
- case TokenKind::EQUALS_PLUS:
- msg += " equals plus '=+'";
- break;
-
- case TokenKind::BAR_EQUALS:
- msg += " bar equals '|='";
- break;
-
- case TokenKind::EXPANSION:
- msg += " expansion";
- break;
-
- case TokenKind::STRING:
- msg += " string";
- break;
-
- case TokenKind::NONE:
- msg += " none";
- break;
- }
-
- error(msg);
- }
-
- std::string BXParser::readIdent(const BXVariables &vars) {
- std::string str;
-
- int ch;
- while ((ch = peek()) != EOF) {
- if (
- (ch >= 'a' && ch <= 'z') ||
- (ch >= 'A' && ch <= 'Z') ||
- (ch == '_')) {
- str.push_back(ch);
- get();
- } else {
- break;
- }
- }
-
- return str;
- }
-
- void BXParser::skipWhitespace() {
- int ch;
- while ((ch = peek()) == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
- get();
- }
- }
-
- char BXParser::readEscape() {
- int ch = get();
- if (ch == EOF) {
- error("Unexpected EOF");
- } else if (ch == 'n') {
- return '\n';
- } else if (ch == 'r') {
- return '\r';
- } else if (ch == 't') {
- return '\t';
- } else {
- return (char)ch;
- }
- }
-
- std::string BXParser::readStringExpansion(const BXVariables &vars) {
- bool braced = peek() == '{';
- std::string key;
- if (braced) {
- get();
- skipWhitespace();
- key = readString(vars);
- } else {
- key = readIdent(vars);
- }
-
- auto it = vars.find(key);
- if (it == vars.end()) {
- error("Key '" + key + "' doesn't exist");
- }
-
- if (braced) {
- skipWhitespace();
-
- if (peek() != '}') {
- error("Expected a '}' after a '${' expansion");
- }
-
- get();
- }
-
- // TODO: Use BXValue.asString()
- return it->second[0];
- }
-
- std::string BXParser::readQuotedString(const BXVariables &vars) {
- std::string str;
-
- int ch;
- while ((ch = peek()) != EOF) {
- if (ch == '\\') {
- get();
- str.push_back(readEscape());
- } else if (ch == '$') {
- get();
- str += readStringExpansion(vars);
- } else if (ch == '"') {
- get();
- break;
- } else {
- str.push_back(ch);
- get();
- }
- }
-
- return str;
- }
-
- std::string BXParser::readString(const BXVariables &vars) {
- std::string str;
-
- int ch;
- while ((ch = peek()) != EOF) {
- if (ch == '\\') {
- get();
- str.push_back(readEscape());
- } else if (ch == '$') {
- get();
- str += readStringExpansion(vars);
- } else if (ch == '"') {
- get();
- str += readQuotedString(vars);
- } else if (
- ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ||
- ch == '}' || ch == ',') {
- break;
- } else {
- str.push_back(ch);
- get();
- }
- }
-
- return str;
- }
-
- BXParser::Token BXParser::getToken(const BXVariables &vars) {
- Token tok;
- tok.line = line();
- tok.ch = ch();
-
- int ch = peek();
-
- if (ch == EOF) {
- tok.kind = TokenKind::E_O_F;
- return tok;
- }
-
- if (ch == '\t' || ch == ' ') {
- tok.kind = TokenKind::INDENTATION;
- do {
- get();
- ch = peek();
- } while (ch == '\t' || ch == ' ');
- return tok;
- } else if (ch == '\n' || ch == '\r') {
- tok.kind = TokenKind::NEWLINE;
- do {
- get();
- ch = peek();
- } while (ch == '\n' || ch == '\r');
- return tok;
- }
-
- int ch2 = peek(2);
- if (ch == ',') {
- get();
- tok.kind = TokenKind::COMMA;
- } else if (ch == ':' && ch2 == '=') {
- get(); get();
- tok.kind = TokenKind::COLON_EQUALS;
- } else if (ch == '+' && ch2 == '=') {
- get(); get();
- tok.kind = TokenKind::PLUS_EQUALS;
- } else if (ch == '=' && ch2 == '+') {
- get(); get();
- tok.kind = TokenKind::EQUALS_PLUS;
- } else if (ch == '|' && ch2 == '=') {
- get(); get();
- tok.kind = TokenKind::BAR_EQUALS;
- } else if (ch == '$' && ch2 == '{') {
- get(); get();
- skipWhitespace();
- tok.kind = TokenKind::EXPANSION;
- tok.str = readString(vars);
- skipWhitespace();
- if (peek() != '}') {
- error("Expected a '}' after a '${' expansion.");
- }
- get();
- } else if (ch == '$') {
- get();
- tok.kind = TokenKind::EXPANSION;
- tok.str = readString(vars);
- } else {
- tok.kind = TokenKind::STRING;
- tok.str = readString(vars);
- }
-
- while ((ch = peek()) == '\t' || ch == ' ') {
- get();
- }
-
- return tok;
- }
-
- BXParser::Token BXParser::readToken(const BXVariables &vars) {
- Token t = tok_;
- tok_ = getToken(vars);
- return t;
- }
-
- void BXWriter::escape(const std::string &str) {
- buf_.put('"');
- for (char ch: str) {
- if (ch == '$' || ch == '"' || ch == '\\') {
- buf_.put('\\');
- }
- buf_.put(ch);
- }
- buf_.put('"');
- }
-
- void BXWriter::write(const BXVariables &vars) {
- for (const auto &pair: vars) {
- size_t chars = 0;
- buf_.put(pair.first);
- buf_.put(" :=");
- for (auto &val: pair.second) {
- if (chars >= 80) {
- buf_.put('\n');
- buf_.put('\t');
- chars = 0;
- } else {
- buf_.put(' ');
- }
-
- escape(val);
- chars += val.size();
- }
- buf_.put('\n');
- }
- }
-
- void BXParser::parse(BXVariables &vars, bool oneLine) {
- readToken(vars);
-
- while (true) {
- if (peekToken().kind == TokenKind::E_O_F) {
- break;
- } else if (peekToken().kind != TokenKind::STRING) {
- error("Expected string, got", peekToken().kind);
- }
-
- Token t = readToken(vars);
- std::string key = t.str;
- std::vector<std::string> &var = vars[key];
-
- void (*addVal)(std::vector<std::string> &var, std::string val);
- switch (peekToken().kind) {
- case TokenKind::COLON_EQUALS:
- var.clear();
-
- // Fallthrough
- case TokenKind::PLUS_EQUALS:
- addVal = [](auto &var, auto val) {
- var.push_back(std::move(val));
- };
- break;
-
- case TokenKind::EQUALS_PLUS:
- addVal = [](auto &var, auto val) {
- var.insert(var.begin(), std::move(val));
- };
- break;
-
- case TokenKind::BAR_EQUALS:
- addVal = [](auto &var, auto val) {
- for (auto &v: var) {
- if (v == val) {
- return;
- }
- }
-
- var.push_back(val);
- };
- break;
-
- default:
- error("Expected operator, got", peekToken().kind);
- }
- readToken(vars);
-
- parseList(vars, var, addVal, oneLine);
- }
- }
-
- void BXParser::parseList(
- BXVariables &vars, std::vector<std::string> &var,
- void (*addVal)(std::vector<std::string> &var, std::string val),
- bool oneLine) {
- while (true) {
- Token tok = peekToken();
- switch (tok.kind) {
- case TokenKind::NEWLINE:
- if (oneLine) {
- return;
- }
-
- readToken(vars);
- if (peekToken().kind != TokenKind::INDENTATION) {
- return;
- }
-
- readToken(vars); // Read indentation
- break;
-
- case TokenKind::STRING:
- addVal(var, std::move(tok.str));
- readToken(vars);
- break;
-
- case TokenKind::COMMA:
- readToken(vars);
- return;
-
- case TokenKind::E_O_F:
- return;
-
- case TokenKind::EXPANSION:
- for (auto &v: vars[tok.str]) {
- addVal(var, v);
- }
- readToken(vars);
- break;
-
- default:
- error("Unexpected token", tok.kind);
- }
- }
- }
-
- void BXParser::parseList(BXVariables &vars, std::vector<std::string> &var) {
- auto addVal = [](auto &var, auto val) {
- var.push_back(std::move(val));
- };
-
- parseList(vars, var, addVal, false);
- }
|