Столкновение типа варианта
Итак, я играл с
Я добавил правило для разбора строковых литералов. Цель состоит в том, чтобы я мог анализировать и компилировать такие программы, как (функциональность уже встроена):
int ret(int x) {
return x;
}
int main() {
int x = 5;
return ret(x)*2;
}
А также (хочу добавить эту функциональность),
string print(string s) {
return s;
}
int main() {
string foo = "bar";
print(foo);
return 0;
}
Независимо от того, скомпилированы ли последние два примера, скажем, с gcc, несущественно.
Итак, суть того, что я добавил, заключается в следующем:
В файле expression_def.hpp (добавлено производственное правило quoted_string):
quoted_string = '"' >> *('\\' >> char_ | ~char_('"')) >> '"'; // ADDED THIS
primary_expr =
uint_
| quoted_string // ADDED THIS
| function_call
| identifier
| bool_
| '(' > expr > ')'
;
в ast.hpp добавлен вариантный тип 'std:string':
typedef boost::variant<
nil
, bool
, unsigned int
, std::string // ADDED THIS
, identifier
, boost::recursive_wrapper<unary>
, boost::recursive_wrapper<function_call>
, boost::recursive_wrapper<expression>
>
operand;
Вот объявление правила для добавления, а также правило, с которым оно сталкивается:
qi::rule<Iterator, std::string(), skipper<Iterator> > identifier;
qi::rule<Iterator, std::string()> quoted_string; // declaring this without the skipper
// lets us avoid the lexeme[] incantation (thanks @sehe).
Проблема сейчас в том, что компилятор путает то, что должно быть "идентификатором" для "quoted_string" - или фактически просто std::string.
Я предполагаю, что причиной проблемы является тот факт, что они оба имеют возвращаемый тип подписи std:: string, но я не знаю хорошего обходного пути здесь. Кроме того, структура 'identifier' имеет член данных типа std:: string, с которым она инициализируется, поэтому на самом деле компилятор не может определить между ними, а вариант std:: string в итоге оказывается лучшим соответствием.
Теперь, если я изменю std:: string на char*, вот так:
typedef boost::variant<
nil
, bool
, unsigned int
, char* // CHANGED, YET AGAIN
, identifier
, boost::recursive_wrapper<unary>
, boost::recursive_wrapper<function_call>
, boost::recursive_wrapper<expression>
>
operand;
он будет компилироваться и работать с целыми числами, поспорим, что я не смогу проанализировать строки (фактически VS вызовет abort()). Следует отметить, что, поскольку каждый вариант нуждается в перегрузке, в моем коде есть что-то вроде:
bool compiler::operator()(std::string const& x)
{
BOOST_ASSERT(current != 0);
current->op(op_string, x);
return true;
}
а также
void function::op(int a, std::string const& b)
{
code.push_back(a);
code.push_back(b.size());
for (uintptr_t ch : b)
{
code.push_back(ch);
}
size_ += 2 + b.size();
}
Они оба работают без сбоев, когда мне нужно разобрать строки (конечно, жертвуя способностью обрабатывать целые числа).
Их целочисленные эквиваленты (и находятся в compiler.cpp)
bool compiler::operator()(unsigned int x)
{
BOOST_ASSERT(current != 0);
current->op(op_int, x);
return true;
}
и конечно:
void function::op(int a, int b)
{
code.push_back(a);
code.push_back(b);
size_ += 2;
}
Если мне нужно изменить тип варианта с std:: string на char*, тогда мне придется обновить перегрузки, и из-за наследства языка C он выглядит немного некрасиво.
Я понимаю, что это может быть немного утомительно и не очень привлекательно, чтобы прочесывать источник, но я уверяю вас, что это действительно не так. Этот урок по компиляции просто помещает байт-код в вектор, который по своей конструкции обрабатывает только целые числа. Я пытаюсь изменить его, чтобы обрабатывать строки, а также, следовательно, дополнения и перегрузки, а также необходимость unintptr_t. Любой, кто знаком с материалом и / или Boost, вероятно, точно знает, на что он смотрит (ehem, @sehe, ehem!).