Browse Source

BBB parser

feature/dependency-graph
Martin Dørum 3 years ago
commit
8bf00395bb
2 changed files with 321 additions and 0 deletions
  1. 269
    0
      src/BBBParser.cc
  2. 52
    0
      src/BBBParser.h

+ 269
- 0
src/BBBParser.cc View File

@@ -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);
}

+ 52
- 0
src/BBBParser.h View File

@@ -0,0 +1,52 @@
#pragma once

#include <unordered_map>
#include <vector>
#include <string>
#include <iostream>
#include <exception>

struct BBBParseError: std::exception {
BBBParseError(std::string msg): message(msg) {}
std::string message;

const char *what() const noexcept override {
return message.c_str();
}
};

class BBBParser {
public:
using Variables = std::unordered_map<std::string, std::vector<std::string>>;

static const int FLAG_NONE = 0;
static const int FLAG_INSECURE = 1 << 0;

BBBParser(std::istream &stream, int flags, int line = 1, int ch = 1):
flags_(flags), line_(line), ch_(ch), stream_(stream) {}

void parseLine(const Variables &vars, std::vector<std::string> &values);
void parse(Variables &vars);

int line_;
int ch_;
int flags_;

private:
int peek() { return stream_.peek(); }
int get();
void skip(char);
void skip() { get(); }
void error(std::string);

void skipWhitespaceLine();
void skipWhitespace();

char parseEscape();
void parseExpansion(const Variables &vars, std::vector<std::string> &values);
void parseQuotedExpansion(const Variables &vars, std::string &content);
void parseQuotedString(const Variables &vars, std::string &content);
bool parseString(const Variables &vars, std::string &content, int sep = -1);

std::istream &stream_;
};

Loading…
Cancel
Save