这是我的代码'main.cpp',我想编译它:
#include <cstdio>
#include "lexer.h"
#include "parser.h"
auto main() -> int {
Parser::binop_precedence = {{'<', 10}, {'+', 20}, {'-', 20}, {'*', 40}};
fprintf(stderr, "ready> ");
Lexer::GetNextToken();
Parser::MainLoop();
return 0;
}
“parse.h”代码如下:
#include <cstdio>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "AST.h"
#include "lexer.h"
class Parser {
public:
// numberexpr ::= number
static auto ParseNumberExpr() -> std::unique_ptr<ExprAST> {
auto result = std::make_unique<NumberExprAST>(Lexer::num_val);
Lexer::GetNextToken();
return std::move(result);
}
static auto ParseExpression() -> std::unique_ptr<ExprAST> {
auto lhs = ParsePrimary();
if (lhs == nullptr) {
return nullptr;
}
return ParseBinOpRhs(0, std::move(lhs));
}
static auto ParseParenExpr() -> std::unique_ptr<ExprAST> {
Lexer::GetNextToken();
auto v = ParseExpression();
if (v == nullptr) {
return nullptr;
}
if (Lexer::cur_tok != ')') {
return LogError("expected ')'");
}
Lexer::GetNextToken();
return v;
}
static auto ParseIdentifierExpr() -> std::unique_ptr<ExprAST> {
std::string id_name = Lexer::identifier_str;
Lexer::GetNextToken();
if (Lexer::cur_tok != '(') {
return std::make_unique<VariableExprAST>(id_name);
}
Lexer::GetNextToken();
std::vector<std::unique_ptr<ExprAST>> args;
if (Lexer::cur_tok != ')') {
while(true) {
if (auto arg = ParseExpression()) {
args.push_back(std::move(arg));
} else {
return nullptr;
}
if (Lexer::cur_tok == ')') {
break;
}
if (Lexer::cur_tok != ',') {
return LogError("Expected ')' or ',' in argument list");
}
Lexer::GetNextToken();
}
}
Lexer::GetNextToken();
return std::make_unique<CallExprAST>(id_name, std::move(args));
}
static auto ParsePrimary() -> std::unique_ptr<ExprAST> {
switch (Lexer::cur_tok) {
default:
return LogError("unknown token when expecting an expression");
case Token::tok_identifier:
return ParseIdentifierExpr();
case Token::tok_number:
return ParseNumberExpr();
case '(' :
return ParseParenExpr();
}
}
public:
// static std::map<char, int> binop_precedence = {{'<', 10}, {'+', 20}, {'-', 20}, {'*', 40}};
static std::map<char, int> binop_precedence;
};
lexer.h
文件如下:
class Lexer {
public:
/// gettok Return the next toker from standard input
static auto GetTok() -> int {
static int last_char = ' ';
// Skip any whitespace
while(std::isspace(last_char) != 0) {
last_char = getchar();
}
if (std::isalpha(last_char) != 0) {
identifier_str += std::to_string(last_char);
while(std::isalnum(last_char = getchar()) != 0) {
identifier_str += std::to_string(last_char);
}
if (identifier_str == "def") {
return Token::tok_def;
}
if (identifier_str == "extern") {
return Token::tok_extern;
}
return Token::tok_identifier;
}
if ((std::isdigit(last_char) != 0) || last_char == '.') {
std::string num_str;
do {
num_str += std::to_string(last_char);
last_char = getchar();
}while ((std::isdigit(last_char) != 0) || last_char == '.');
num_val = std::strtod(num_str.c_str(), nullptr);
return Token::tok_number;
}
// 处理注释
if (last_char == '#') {
do {
last_char = getchar();
}while(last_char != EOF && last_char != '\n' && last_char != '\r');
if (last_char != EOF) {
return GetTok();
}
}
if (last_char == EOF) {
return Token::tok_eof;
}
int this_char = last_char;
last_char = getchar();
return this_char;
}
static auto GetNextToken() -> int {
return cur_tok = Lexer::GetTok();
}
public:
static std::string identifier_str; // Filled in if tok_identifier
static double num_val; // Filled in tok_number
static int cur_tok;
};
我想使用cmake来编译
main.cpp
并链接parse.h
文件。我的CMakeLists.txt
是这样的:
cmake_minimum_required(VERSION 3.10)
set (CMAKE_CXX_FLAGS -std=c++20)
SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) # for clangd
set(CMAKE_CXX_STANDARD 20) # compile as C++20
set(CMAKE_CXX_STANDARD_REQUIRED 20) # require C++20 support
project(Kaleidoscope)
message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
message(STATUS "CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_FXX_FLAGS_DEBUG}")
# includes
set(KALEDIOSCOPE_SRC_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
include_directories(${KALEDIOSCOPE_SRC_INCLUDE_DIR})
SET(KALEIDOSCOPE_LIBS log)
add_executable(Kaleidoscope src/lexer.cpp src/parse.cpp src/log.cpp src/main.cpp)
target_include_directories(Kaleidoscope PRIVATE ${PROJECT_SOURCE_DIR}/include)
当我编译它时,编译器告诉我
/usr/bin/ld: CMakeFiles/Kaleidoscope.dir/src/main.cpp.o: in function `main':
main.cpp:(.text+0x56): undefined reference to `Parser::binop_precedence'
/usr/bin/ld: CMakeFiles/Kaleidoscope.dir/src/main.cpp.o: in function `Lexer::GetNextToken()':
main.cpp:(.text._ZN5Lexer12GetNextTokenEv[_ZN5Lexer12GetNextTokenEv]+0xc): undefined reference to `Lexer::cur_tok'
/usr/bin/ld: CMakeFiles/Kaleidoscope.dir/src/main.cpp.o: in function `Parser::MainLoop()':
main.cpp:(.text._ZN6Parser8MainLoopEv[_ZN6Parser8MainLoopEv]+0x28): undefined reference to `Lexer::cur_tok'
/usr/bin/ld: CMakeFiles/Kaleidoscope.dir/src/main.cpp.o: in function `Lexer::GetTok()':
main.cpp:(.text._ZN5Lexer6GetTokEv[_ZN5Lexer6GetTokEv]+0x63): undefined reference to `Lexer::identifier_str[abi:cxx11]'
/usr/bin/ld: main.cpp:(.text._ZN5Lexer6GetTokEv[_ZN5Lexer6GetTokEv]+0xb5): undefined reference to `Lexer::identifier_str[abi:cxx11]'
/usr/bin/ld: main.cpp:(.text._ZN5Lexer6GetTokEv[_ZN5Lexer6GetTokEv]+0x108): undefined reference to `Lexer::identifier_str[abi:cxx11]'
/usr/bin/ld: main.cpp:(.text._ZN5Lexer6GetTokEv[_ZN5Lexer6GetTokEv]+0x134): undefined reference to `Lexer::identifier_str[abi:cxx11]'
/usr/bin/ld: main.cpp:(.text._ZN5Lexer6GetTokEv[_ZN5Lexer6GetTokEv]+0x23b): undefined reference to `Lexer::num_val'
/usr/bin/ld: CMakeFiles/Kaleidoscope.dir/src/main.cpp.o: in function `Parser::ParsePrototype()':
main.cpp:(.text._ZN6Parser14ParsePrototypeEv[_ZN6Parser14ParsePrototypeEv]+0x1a): undefined reference to `Lexer::cur_tok'
/usr/bin/ld: main.cpp:
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [CMakeFiles/Kaleidoscope.dir/build.make:145: Kaleidoscope] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/Kaleidoscope.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
我想知道如何编写CMakeLists.txt来处理这个问题。谢谢!
您正在引用
Parser::binop_precedence
而没有它的实例。
您需要在全局命名空间中创建实例,最好是在
parse.cpp
中:
std::map<char, int> Parser::binop_precedence;