#include "input_buffer.hh"
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <functional>
#ifndef NDEBUG
#include <iostream>
#endif
#include <limits>
#include <sys/stat.h>
#include <sys/mman.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef MAP_PREFAULT_READ
#define MAP_PREFAULT_READ 0
#endif
using std::string;
namespace
{
struct mmap_input_buffer : public dtc::input_buffer
{
string fn;
const string &filename() const override
{
return fn;
}
mmap_input_buffer(int fd, string &&filename);
virtual ~mmap_input_buffer();
};
struct stream_input_buffer : public dtc::input_buffer
{
const string &filename() const override
{
static string n = "<standard input>";
return n;
}
std::vector<char> b;
stream_input_buffer();
};
mmap_input_buffer::mmap_input_buffer(int fd, string &&filename)
: input_buffer(0, 0), fn(filename)
{
struct stat sb;
if (fstat(fd, &sb))
{
perror("Failed to stat file");
}
size = sb.st_size;
buffer = (const char*)mmap(0, size, PROT_READ, MAP_PRIVATE |
MAP_PREFAULT_READ, fd, 0);
if (buffer == MAP_FAILED)
{
perror("Failed to mmap file");
exit(EXIT_FAILURE);
}
}
mmap_input_buffer::~mmap_input_buffer()
{
if (buffer != 0)
{
munmap(const_cast<char*>(buffer), size);
}
}
stream_input_buffer::stream_input_buffer() : input_buffer(0, 0)
{
int c;
while ((c = fgetc(stdin)) != EOF)
{
b.push_back(c);
}
buffer = b.data();
size = b.size();
}
}
namespace dtc
{
void
input_buffer::skip_to(char c)
{
while ((cursor < size) && (buffer[cursor] != c))
{
cursor++;
}
}
void
text_input_buffer::skip_to(char c)
{
while (!finished() && (*(*this) != c))
{
++(*this);
}
}
void
text_input_buffer::skip_spaces()
{
if (finished()) { return; }
char c = *(*this);
bool last_nl = false;
while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f')
|| (c == '\v') || (c == '\r'))
{
last_nl = ((c == '\n') || (c == '\r'));
++(*this);
if (finished())
{
c = '\0';
}
else
{
c = *(*this);
}
}
if ((c == '#') && ((cursor == 0) || last_nl))
{
skip_to('\n');
skip_spaces();
}
if (consume("/include/"))
{
handle_include();
skip_spaces();
}
}
void
text_input_buffer::handle_include()
{
bool reallyInclude = true;
if (consume("if "))
{
next_token();
string name = parse_property_name();
if (defines.count(name) == 0)
{
reallyInclude = false;
}
consume('/');
}
next_token();
if (!consume('"'))
{
parse_error("Expected quoted filename");
return;
}
auto loc = location();
string file = parse_to('"');
consume('"');
if (!reallyInclude)
{
return;
}
string include_file = dir + '/' + file;
auto include_buffer = input_buffer::buffer_for_file(include_file, false);
if (include_buffer == 0)
{
for (auto i : include_paths)
{
include_file = i + '/' + file;
include_buffer = input_buffer::buffer_for_file(include_file, false);
if (include_buffer != 0)
{
break;
}
}
}
if (depfile)
{
putc(' ', depfile);
fputs(include_file.c_str(), depfile);
}
if (!include_buffer)
{
loc.report_error("Unable to locate input file");
return;
}
input_stack.push(std::move(include_buffer));
}
bool text_input_buffer::read_binary_file(const std::string &filename, byte_buffer &b)
{
bool try_include_paths = true;
string include_file;
if (filename[0] == '/')
{
include_file = filename;
try_include_paths = false;
}
else
{
include_file = dir + '/' + filename;
}
auto include_buffer = input_buffer::buffer_for_file(include_file, false);
if (include_buffer == 0 && try_include_paths)
{
for (auto i : include_paths)
{
include_file = i + '/' + filename;
include_buffer = input_buffer::buffer_for_file(include_file, false);
if (include_buffer != 0)
{
break;
}
}
}
if (!include_buffer)
{
return false;
}
if (depfile)
{
putc(' ', depfile);
fputs(include_file.c_str(), depfile);
}
b.insert(b.begin(), include_buffer->begin(), include_buffer->end());
return true;
}
input_buffer
input_buffer::buffer_from_offset(int offset, int s)
{
if (offset < 0)
{
return input_buffer();
}
if (s == 0)
{
s = size - offset;
}
if (offset > size)
{
return input_buffer();
}
if (s > (size-offset))
{
return input_buffer();
}
return input_buffer(&buffer[offset], s);
}
bool
input_buffer::consume(const char *str)
{
int len = strlen(str);
if (len > size - cursor)
{
return false;
}
else
{
for (int i=0 ; i<len ; ++i)
{
if (str[i] != (*this)[i])
{
return false;
}
}
cursor += len;
return true;
}
return false;
}
bool
input_buffer::consume_char_literal(unsigned long long &outInt)
{
outInt = (unsigned char)((*this)[0]);
cursor++;
if(outInt != '\\')
{
return true;
}
else if(cursor >= size)
{
return false;
}
outInt = (unsigned char)((*this)[0]);
cursor++;
switch (outInt) {
default:
return false;
case 'n':
outInt = (unsigned char)'\n';
break;
case 'r':
outInt = (unsigned char)'\r';
break;
case 't':
outInt = (unsigned char)'\t';
break;
case '0':
outInt = 0;
break;
case '\'':
case '\\':
break;
}
return true;
}
bool
input_buffer::consume_integer(unsigned long long &outInt)
{
if (!isdigit((*this)[0]))
{
return false;
}
char *end= const_cast<char*>(&buffer[size]);
errno = 0;
outInt = strtoull(&buffer[cursor], &end, 0);
if (end == &buffer[cursor] ||
(outInt == std::numeric_limits<unsigned long long>::max() &&
errno == ERANGE))
{
return false;
}
cursor = end - buffer;
return true;
}
namespace {
typedef unsigned long long valty;
struct expression
{
typedef text_input_buffer::source_location source_location;
typedef std::pair<valty, bool> result;
virtual result operator()() = 0;
virtual int precedence() = 0;
expression(source_location l) : loc(l) {}
virtual ~expression() {}
#ifndef NDEBUG
void dump(bool nl=false)
{
void *ptr = this;
if (ptr == nullptr)
{
std::cerr << "{nullptr}\n";
return;
}
dump_impl();
if (nl)
{
std::cerr << '\n';
}
}
private:
virtual void dump_impl() = 0;
#endif
protected:
source_location loc;
};
class terminal_expr : public expression
{
valty val;
result operator()() override
{
return {val, true};
}
int precedence() override
{
return 0;
}
public:
terminal_expr(source_location l, valty v) : expression(l), val(v) {}
#ifndef NDEBUG
void dump_impl() override { std::cerr << val; }
#endif
};
struct paren_expression : public expression
{
expression_ptr subexpr;
paren_expression(source_location l, expression_ptr p) : expression(l),
subexpr(std::move(p)) {}
int precedence() override
{
return 0;
}
result operator()() override
{
return (*subexpr)();
}
#ifndef NDEBUG
void dump_impl() override
{
std::cerr << " (";
subexpr->dump();
std::cerr << ") ";
}
#endif
};
template<char OpChar, class Op>
class unary_operator : public expression
{
expression_ptr subexpr;
result operator()() override
{
Op op;
result s = (*subexpr)();
if (!s.second)
{
return s;
}
return {op(s.first), true};
}
int precedence() override
{
return 3;
}
public:
unary_operator(source_location l, expression_ptr p) :
expression(l), subexpr(std::move(p)) {}
#ifndef NDEBUG
void dump_impl() override
{
std::cerr << OpChar;
subexpr->dump();
}
#endif
};
struct binary_operator_base : public expression
{
using expression::expression;
expression_ptr lhs;
expression_ptr rhs;
void insert_left(binary_operator_base *new_left)
{
if (lhs->precedence() < new_left->precedence())
{
new_left->rhs = std::move(lhs);
lhs.reset(new_left);
}
else
{
static_cast<binary_operator_base*>(lhs.get())->insert_left(new_left);
}
}
};
template<int Precedence, class Op>
struct binary_operator : public binary_operator_base
{
result operator()() override
{
Op op;
result l = (*lhs)();
result r = (*rhs)();
if (!(l.second && r.second))
{
return {0, false};
}
return {op(l.first, r.first), true};
}
int precedence() override
{
return Precedence;
}
#ifdef NDEBUG
binary_operator(source_location l, const char *) :
binary_operator_base(l) {}
#else
const char *opName;
binary_operator(source_location l, const char *o) :
binary_operator_base(l), opName(o) {}
void dump_impl() override
{
lhs->dump();
std::cerr << opName;
rhs->dump();
}
#endif
};
class ternary_conditional_operator : public expression
{
expression_ptr cond;
expression_ptr lhs;
expression_ptr rhs;
result operator()() override
{
result c = (*cond)();
result l = (*lhs)();
result r = (*rhs)();
if (!(l.second && r.second && c.second))
{
return {0, false};
}
return c.first ? l : r;
}
int precedence() override
{
return 3;
}
#ifndef NDEBUG
void dump_impl() override
{
cond->dump();
std::cerr << " ? ";
lhs->dump();
std::cerr << " : ";
rhs->dump();
}
#endif
public:
ternary_conditional_operator(source_location sl,
expression_ptr c,
expression_ptr l,
expression_ptr r) :
expression(sl), cond(std::move(c)), lhs(std::move(l)),
rhs(std::move(r)) {}
};
template<typename T>
struct lshift
{
constexpr T operator()(const T &lhs, const T &rhs) const
{
return lhs << rhs;
}
};
template<typename T>
struct rshift
{
constexpr T operator()(const T &lhs, const T &rhs) const
{
return lhs >> rhs;
}
};
template<typename T>
struct unary_plus
{
constexpr T operator()(const T &val) const
{
return +val;
}
};
template<typename T>
struct bit_not
{
constexpr T operator()(const T &val) const
{
return ~val;
}
};
template<typename T>
struct divmod : public binary_operator<5, T>
{
using binary_operator<5, T>::binary_operator;
using typename binary_operator_base::result;
result operator()() override
{
result r = (*binary_operator_base::rhs)();
if (r.second && (r.first == 0))
{
expression::loc.report_error("Division by zero");
return {0, false};
}
return binary_operator<5, T>::operator()();
}
};
}
expression_ptr text_input_buffer::parse_binary_expression(expression_ptr lhs)
{
next_token();
binary_operator_base *expr = nullptr;
char op = *(*this);
source_location l = location();
switch (op)
{
default:
return lhs;
case '+':
expr = new binary_operator<6, std::plus<valty>>(l, "+");
break;
case '-':
expr = new binary_operator<6, std::minus<valty>>(l, "-");
break;
case '%':
expr = new divmod<std::modulus<valty>>(l, "/");
break;
case '*':
expr = new binary_operator<5, std::multiplies<valty>>(l, "*");
break;
case '/':
expr = new divmod<std::divides<valty>>(l, "/");
break;
case '<':
switch (peek())
{
default:
parse_error("Invalid operator");
return nullptr;
case ' ':
case '(':
case '0'...'9':
expr = new binary_operator<8, std::less<valty>>(l, "<");
break;
case '=':
++(*this);
expr = new binary_operator<8, std::less_equal<valty>>(l, "<=");
break;
case '<':
++(*this);
expr = new binary_operator<7, lshift<valty>>(l, "<<");
break;
}
break;
case '>':
switch (peek())
{
default:
parse_error("Invalid operator");
return nullptr;
case '(':
case ' ':
case '0'...'9':
expr = new binary_operator<8, std::greater<valty>>(l, ">");
break;
case '=':
++(*this);
expr = new binary_operator<8, std::greater_equal<valty>>(l, ">=");
break;
case '>':
++(*this);
expr = new binary_operator<7, rshift<valty>>(l, ">>");
break;
return lhs;
}
break;
case '=':
if (peek() != '=')
{
parse_error("Invalid operator");
return nullptr;
}
expr = new binary_operator<9, std::equal_to<valty>>(l, "==");
break;
case '!':
if (peek() != '=')
{
parse_error("Invalid operator");
return nullptr;
}
cursor++;
expr = new binary_operator<9, std::not_equal_to<valty>>(l, "!=");
break;
case '&':
if (peek() == '&')
{
expr = new binary_operator<13, std::logical_and<valty>>(l, "&&");
}
else
{
expr = new binary_operator<10, std::bit_and<valty>>(l, "&");
}
break;
case '|':
if (peek() == '|')
{
expr = new binary_operator<12, std::logical_or<valty>>(l, "||");
}
else
{
expr = new binary_operator<14, std::bit_or<valty>>(l, "|");
}
break;
case '?':
{
consume('?');
expression_ptr true_case = parse_expression();
next_token();
if (!true_case || !consume(':'))
{
parse_error("Expected : in ternary conditional operator");
return nullptr;
}
expression_ptr false_case = parse_expression();
if (!false_case)
{
parse_error("Expected false condition for ternary operator");
return nullptr;
}
return expression_ptr(new ternary_conditional_operator(l, std::move(lhs),
std::move(true_case), std::move(false_case)));
}
}
++(*this);
next_token();
expression_ptr e(expr);
expression_ptr rhs(parse_expression());
if (!rhs)
{
return nullptr;
}
expr->lhs = std::move(lhs);
if (rhs->precedence() < expr->precedence())
{
expr->rhs = std::move(rhs);
}
else
{
binary_operator_base *rhs_op =
static_cast<binary_operator_base*>(rhs.get());
rhs_op->insert_left(expr);
e.release();
return rhs;
}
return e;
}
expression_ptr text_input_buffer::parse_expression(bool stopAtParen)
{
next_token();
unsigned long long leftVal;
expression_ptr lhs;
source_location l = location();
switch (*(*this))
{
case '\'':
consume('\'');
if(!consume_char_literal(leftVal))
{
return nullptr;
}
if (!consume('\''))
{
return nullptr;
}
lhs.reset(new terminal_expr(l, leftVal));
break;
case '0'...'9':
if (!consume_integer(leftVal))
{
return nullptr;
}
lhs.reset(new terminal_expr(l, leftVal));
break;
case '(':
{
consume('(');
expression_ptr &&subexpr = parse_expression();
if (!subexpr)
{
return nullptr;
}
lhs.reset(new paren_expression(l, std::move(subexpr)));
if (!consume(')'))
{
return nullptr;
}
if (stopAtParen)
{
return lhs;
}
break;
}
case '+':
{
consume('+');
expression_ptr &&subexpr = parse_expression();
if (!subexpr)
{
return nullptr;
}
lhs.reset(new unary_operator<'+', unary_plus<valty>>(l, std::move(subexpr)));
break;
}
case '-':
{
consume('-');
expression_ptr &&subexpr = parse_expression();
if (!subexpr)
{
return nullptr;
}
lhs.reset(new unary_operator<'-', std::negate<valty>>(l, std::move(subexpr)));
break;
}
case '!':
{
consume('!');
expression_ptr &&subexpr = parse_expression();
if (!subexpr)
{
return nullptr;
}
lhs.reset(new unary_operator<'!', std::logical_not<valty>>(l, std::move(subexpr)));
break;
}
case '~':
{
consume('~');
expression_ptr &&subexpr = parse_expression();
if (!subexpr)
{
return nullptr;
}
lhs.reset(new unary_operator<'~', bit_not<valty>>(l, std::move(subexpr)));
break;
}
}
if (!lhs)
{
return nullptr;
}
return parse_binary_expression(std::move(lhs));
}
bool
text_input_buffer::consume_integer_expression(unsigned long long &outInt)
{
switch (*(*this))
{
case '(':
{
expression_ptr e(parse_expression(true));
if (!e)
{
return false;
}
auto r = (*e)();
if (r.second)
{
outInt = r.first;
return true;
}
return false;
}
case '0'...'9':
return consume_integer(outInt);
default:
return false;
}
}
bool
input_buffer::consume_hex_byte(uint8_t &outByte)
{
if (!ishexdigit((*this)[0]) && !ishexdigit((*this)[1]))
{
return false;
}
outByte = (digittoint((*this)[0]) << 4) | digittoint((*this)[1]);
cursor += 2;
return true;
}
text_input_buffer&
text_input_buffer::next_token()
{
auto &self = *this;
int start;
do {
start = cursor;
skip_spaces();
if (finished())
{
return self;
}
if (*self == '/' && peek() == '*')
{
++self;
++self;
do {
while ((*self != '\0') && (*self != '*') && !finished())
{
++self;
}
++self;
} while ((*self != '\0') && (*self != '/') && !finished());
++self;
}
if ((*self == '/' && peek() == '/'))
{
++self;
++self;
while (*self != '\n' && !finished())
{
++self;
}
++self;
}
} while (start != cursor);
return self;
}
void
text_input_buffer::parse_error(const char *msg)
{
if (input_stack.empty())
{
fprintf(stderr, "Error: %s\n", msg);
return;
}
input_buffer &b = *input_stack.top();
parse_error(msg, b, b.cursor);
}
void
text_input_buffer::parse_error(const char *msg,
input_buffer &b,
int loc)
{
int line_count = 1;
int line_start = 0;
int line_end = loc;
if (loc < 0 || loc > b.size)
{
return;
}
for (int i=loc ; i>0 ; --i)
{
if (b.buffer[i] == '\n')
{
line_count++;
if (line_start == 0)
{
line_start = i+1;
}
}
}
for (int i=loc+1 ; i<b.size ; ++i)
{
if (b.buffer[i] == '\n')
{
line_end = i;
break;
}
}
fprintf(stderr, "Error at %s:%d:%d: %s\n", b.filename().c_str(), line_count, loc - line_start, msg);
fwrite(&b.buffer[line_start], line_end-line_start, 1, stderr);
putc('\n', stderr);
for (int i=0 ; i<(loc-line_start) ; ++i)
{
char c = (b.buffer[i+line_start] == '\t') ? '\t' : ' ';
putc(c, stderr);
}
putc('^', stderr);
putc('\n', stderr);
}
#ifndef NDEBUG
void
input_buffer::dump()
{
fprintf(stderr, "Current cursor: %d\n", cursor);
fwrite(&buffer[cursor], size-cursor, 1, stderr);
}
#endif
namespace
{
struct is_alpha
{
static inline bool check(const char c)
{
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') &&
(c <= 'Z'));
}
};
struct is_node_name_character
{
static inline bool check(const char c)
{
switch(c)
{
default:
return false;
case 'a'...'z': case 'A'...'Z': case '0'...'9':
case ',': case '.': case '+': case '-':
case '_':
return true;
}
}
};
struct is_property_name_character
{
static inline bool check(const char c)
{
switch(c)
{
default:
return false;
case 'a'...'z': case 'A'...'Z': case '0'...'9':
case ',': case '.': case '+': case '-':
case '_': case '#':
return true;
}
}
};
template<class T>
string parse(text_input_buffer &s)
{
std::vector<char> bytes;
for (char c=*s ; T::check(c) ; c=*(++s))
{
bytes.push_back(c);
}
return string(bytes.begin(), bytes.end());
}
}
string
text_input_buffer::parse_node_name()
{
return parse<is_node_name_character>(*this);
}
string
text_input_buffer::parse_property_name()
{
return parse<is_property_name_character>(*this);
}
string
text_input_buffer::parse_node_or_property_name(bool &is_property)
{
if (is_property)
{
return parse_property_name();
}
std::vector<char> bytes;
for (char c=*(*this) ; is_node_name_character::check(c) ; c=*(++(*this)))
{
bytes.push_back(c);
}
for (char c=*(*this) ; is_property_name_character::check(c) ; c=*(++(*this)))
{
bytes.push_back(c);
is_property = true;
}
return string(bytes.begin(), bytes.end());
}
string
input_buffer::parse_to(char stop)
{
std::vector<char> bytes;
for (char c=*(*this) ; c != stop ; c=*(++(*this)))
{
bytes.push_back(c);
}
return string(bytes.begin(), bytes.end());
}
string
text_input_buffer::parse_to(char stop)
{
std::vector<char> bytes;
for (char c=*(*this) ; c != stop ; c=*(++(*this)))
{
if (finished())
{
break;
}
bytes.push_back(c);
}
return string(bytes.begin(), bytes.end());
}
char
text_input_buffer::peek()
{
return (*input_stack.top())[1];
}
std::unique_ptr<input_buffer>
input_buffer::buffer_for_file(const string &path, bool warn)
{
if (path == "-")
{
std::unique_ptr<input_buffer> b(new stream_input_buffer());
return b;
}
int source = open(path.c_str(), O_RDONLY);
if (source == -1)
{
if (warn)
{
fprintf(stderr, "Unable to open file '%s'. %s\n", path.c_str(), strerror(errno));
}
return 0;
}
struct stat st;
if (fstat(source, &st) == 0 && S_ISDIR(st.st_mode))
{
if (warn)
{
fprintf(stderr, "File %s is a directory\n", path.c_str());
}
close(source);
return 0;
}
std::unique_ptr<input_buffer> b(new mmap_input_buffer(source, string(path)));
close(source);
return b;
}
}