blob: f91e5c68897a09bacec0f37017b06d05cc2b676e [file] [log] [blame]
//===-- macro-parser.cpp - definitions for godumpspec macro parser --------===//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
//
// Definitions for MacroParser class
//
//===----------------------------------------------------------------------===//
#include "macro-parser.h"
#include <iostream>
#include <vector>
#include <algorithm>
int MacroParser::visitMacroLine(const std::string &line, unsigned lno)
{
// Chop off the initial "#define "
if (std::strncmp(line.c_str(), "#define ", 8) != 0) {
llvm::errs() << "malformed macro input at line "
<< lno << ": " << line << "\n";
return 1;
}
std::string mac(line.substr(8));
auto spos = mac.find(' ');
if (spos == std::string::npos) {
llvm::errs() << "malformed macro input at line "
<< lno << ": " << line << "\n";
return 1;
}
// Divide into macro name and macro body
MacroDef d;
d.name = mac.substr(0, spos);
d.body = mac.substr(spos+1);
// Don't try to process functions.
auto ppos = d.name.find('(');
if (ppos != std::string::npos)
return 0;
// A collision here typically indicates that we have
// a clash between a macro and an enum literal.
auto it = macros_.find(d);
if (it != macros_.end()) {
assert(it->enumDef);
return 0;
}
// Add entry to macro table for later post-processing
d.enumDef = false;
macros_.insert(d);
return 0;
}
void MacroParser::addEnumLiteralPseudoMacro(const std::string &name,
const std::string &value)
{
MacroDef d;
d.name = name;
d.body = value;
d.enumDef = true;
assert(macros_.find(d) == macros_.end());
macros_.insert(d);
}
// This method parses the body of the specified macro so as to decide
// whether it can be represented as a Go constant. This means
// decomposing it into tokens and then insuring that the expression it
// represents is suitable for Go. Macros are allowed to refer to other
// macros, as we discover references to other macros as part of the
// process we visit them as well. If a macro body looks OK, we fill in
// the "expanded" data member with its canonicalized representation.
void MacroParser::visitMacro(MacroDef *m)
{
switch(m->mstate) {
case VisitInProgress:
// Cycles not allowed.
m->mstate = VisitedWithError;
return;
case VisitedOK:
case VisitedEmpty:
case VisitedWithError:
// already processed
return;
case Unvisited:
m->mstate = VisitInProgress;
break;
default:
assert(false);
}
MacroTokenizer t(m->body);
std::string str;
llvm::raw_string_ostream os(str);
bool eof = false;
bool error = false;
bool saw_operand = false;
bool expect_operand = false;
while(!error && !eof) {
std::pair<MacTokenTyp, std::string> tv = t.getToken();
switch(tv.first) {
case TOK_ERROR:
error = true;
break;
case TOK_END_OF_STRING:
eof = true;
break;
case TOK_IDENTIFIER: {
if (saw_operand) {
error = true;
break;
}
MacroDef *m2 = lookup(tv.second);
if (m2 == nullptr) {
// Not a macro -- bail here
error = true;
break;
}
visitMacro(m2);
if (m2->mstate == VisitedWithError) {
error = true;
break;
}
if (m2->mstate == VisitedEmpty) {
// Nothing to emit here
} else {
assert(m2->mstate == VisitedOK);
os << "_" << m2->name;
saw_operand = true;
expect_operand = false;
}
break;
}
case TOK_NUMERIC_CONSTANT:
os << tv.second;
saw_operand = true;
expect_operand = false;
break;
case TOK_SPACE:
os << tv.second;
break;
case TOK_ADDSUB:
saw_operand = false;
os << tv.second;
break;
case TOK_UNOP:
if (saw_operand) {
error = true;
break;
}
expect_operand = true;
os << tv.second;
break;
case TOK_BINOP:
if (!saw_operand) {
error = true;
break;
}
saw_operand = false;
expect_operand = true;
os << tv.second;
break;
case TOK_OPEN_PAREN:
saw_operand = false;
expect_operand = false;
os << tv.second;
break;
case TOK_CLOSE_PAREN:
if (expect_operand) {
error = true;
break;
}
saw_operand = true;
os << tv.second;
break;
case TOK_STRING_CONSTANT:
if (saw_operand) {
error = true;
break;
}
saw_operand = true;
expect_operand = false;
os << tv.second;
break;
default:
error = true;
break;
}
}
if (error || expect_operand) {
m->mstate = VisitedWithError;
return;
}
m->expanded = os.str();
auto notEmpty = m->expanded.find_first_not_of(" \t");
if (notEmpty == std::string::npos) {
m->mstate = VisitedEmpty;
} else {
m->mstate = VisitedOK;
}
}
void MacroParser::postProcessMacros()
{
for (auto it = macros_.begin(); it != macros_.end(); it++)
visitMacro(const_cast<MacroDef*>(&(*it)));
}
void MacroParser::emitMacros(llvm::raw_ostream &os,
const std::unordered_set<std::string> &emittedTypes)
{
// Collect all emittable macros
std::vector<MacroDef *> defs;
for (auto it = macros_.begin(); it != macros_.end(); it++) {
MacroDef *def = const_cast<MacroDef*>(&(*it));
// Weed out macros that had problems in parsing.
if (def->mstate != VisitedOK)
continue;
// Types take precedence over macros
if (emittedTypes.find(def->name) != emittedTypes.end())
continue;
defs.push_back(def);
}
// Sort by name for nicer output
std::sort(defs.begin(), defs.end(),
[](MacroDef *d1, MacroDef *d2) {
return d1->name.compare(d2->name) < 0;
});
// Output a Go equivalent.
for (auto it = defs.begin(); it != defs.end(); it++) {
MacroDef *d = (*it);
os << "const _" << d->name << " = " << d->expanded << "\n";
}
}