|
|
@@ -0,0 +1,269 @@ |
|
|
|
#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); |
|
|
|
} |