scanner的设计与实现,C++实现,给出代码
时间: 2023-07-15 11:12:16 浏览: 182
Scanner 是编译器中的一个重要组成部分,用于将源代码转化为词法单元流,也就是 token。下面是一个简单的 Scanner 的实现,使用 C++语言。
```cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
enum TokenType {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING,
TYPE_IDENTIFIER,
TYPE_UNKNOWN
};
struct Token {
TokenType type;
string value;
};
class Scanner {
public:
Scanner(const string& source) : source_(source), current_(0) {}
vector<Token> scanTokens() {
while (!isAtEnd()) {
start_ = current_;
scanToken();
}
tokens_.push_back(Token{TYPE_UNKNOWN, "",});
return tokens_;
}
private:
void scanToken() {
char c = advance();
switch (c) {
case '(': addToken(TokenType::LEFT_PAREN); break;
case ')': addToken(TokenType::RIGHT_PAREN); break;
case '{': addToken(TokenType::LEFT_BRACE); break;
case '}': addToken(TokenType::RIGHT_BRACE); break;
case ',': addToken(TokenType::COMMA); break;
case '.': addToken(TokenType::DOT); break;
case '-': addToken(TokenType::MINUS); break;
case '+': addToken(TokenType::PLUS); break;
case ';': addToken(TokenType::SEMICOLON); break;
case '*': addToken(TokenType::STAR); break;
case '!': addToken(match('=') ? TokenType::BANG_EQUAL : TokenType::BANG); break;
case '=': addToken(match('=') ? TokenType::EQUAL_EQUAL : TokenType::EQUAL); break;
case '<': addToken(match('=') ? TokenType::LESS_EQUAL : TokenType::LESS); break;
case '>': addToken(match('=') ? TokenType::GREATER_EQUAL : TokenType::GREATER); break;
case '/':
if (match('/')) {
while (peek() != '\n' && !isAtEnd()) advance();
} else {
addToken(TokenType::SLASH);
}
break;
case ' ':
case '\r':
case '\t':
// Ignore whitespace.
break;
case '\n':
line_++;
break;
case '"': string(); break;
default:
if (isdigit(c)) {
number();
} else if (isalpha(c)) {
identifier();
} else {
addToken(TokenType::UNKNOWN);
}
break;
}
}
void string() {
while (peek() != '"' && !isAtEnd()) {
if (peek() == '\n') line_++;
advance();
}
if (isAtEnd()) {
// Unterminated string.
return addToken(TokenType::UNKNOWN);
}
// The closing ".
advance();
// Trim the surrounding quotes.
string value = source_.substr(start_ + 1, current_ - start_ - 2);
addToken(TokenType::STRING, value);
}
void number() {
while (isdigit(peek())) advance();
// Look for a fractional part.
if (peek() == '.' && isdigit(peekNext())) {
// Consume the ".".
advance();
while (isdigit(peek())) advance();
}
addToken(TokenType::NUMBER, source_.substr(start_, current_ - start_));
}
void identifier() {
while (isalnum(peek())) advance();
string text = source_.substr(start_, current_ - start_);
TokenType type = keywords_[text];
if (type == TokenType::UNKNOWN) {
type = TokenType::IDENTIFIER;
}
addToken(type);
}
bool match(char expected) {
if (isAtEnd()) return false;
if (source_[current_] != expected) return false;
current_++;
return true;
}
char peek() const {
if (isAtEnd()) return '\0';
return source_[current_];
}
char peekNext() const {
if (current_ + 1 >= source_.size()) return '\0';
return source_[current_ + 1];
}
char advance() {
current_++;
return source_[current_ - 1];
}
void addToken(TokenType type) {
addToken(type, "");
}
void addToken(TokenType type, const string& literal) {
string text = source_.substr(start_, current_ - start_);
tokens_.push_back(Token{type, literal});
}
bool isAtEnd() const {
return current_ >= source_.size();
}
private:
const string source_;
vector<Token> tokens_;
int start_ = 0;
int current_ = 0;
int line_ = 1;
static const unordered_map<string, TokenType> keywords_;
};
const unordered_map<string, TokenType> Scanner::keywords_ = {
{"and", TokenType::AND},
{"class", TokenType::CLASS},
{"else", TokenType::ELSE},
{"false", TokenType::FALSE},
{"for", TokenType::FOR},
{"fun", TokenType::FUN},
{"if", TokenType::IF},
{"nil", TokenType::NIL},
{"or", TokenType::OR},
{"print", TokenType::PRINT},
{"return", TokenType::RETURN},
{"super", TokenType::SUPER},
{"this", TokenType::THIS},
{"true", TokenType::TRUE},
{"var", TokenType::VAR},
{"while", TokenType::WHILE},
};
```
这个 Scanner 实现了一些基本的词法单元,如括号、运算符、数字、字符串和标识符等。在 `scanToken()` 方法中,我们遍历源代码的每个字符,根据不同的字符类型调用不同的方法来处理。例如,如果当前字符是数字,则调用 `number()` 方法来解析数字,如果是字母,则调用 `identifier()` 方法来解析标识符。最后,将解析出的 token 放入 `tokens_` 中返回。
当然,这只是一个简单的实现,实际上编译器的 Scanner 还有很多复杂的特性,如注释、换行符、转义字符等等。
阅读全文