| //===-- 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"; |
| } |
| } |