一個禮拜之後我終於從成都回來了, 從今天開始更新會恢復... 一點小的改進 寫 的時候距離我上一次寫已經一個禮拜了, 所以我回顧了一下之前的代碼, 發現還是有瑕疵. 比如考慮到一個較短的程式, 短到小於BUFFERSIZE(256), 這時其實我的程式是有錯的, 因為此時 中的內容有一部分是未定義的 ...
一個禮拜之後我終於從成都回來了, 從今天開始更新會恢復...
一點小的改進
寫lex()
的時候距離我上一次寫已經一個禮拜了, 所以我回顧了一下之前的代碼, 發現還是有瑕疵. 比如考慮到一個較短的程式, 短到小於BUFFERSIZE(256), 這時其實我的程式是有錯的, 因為此時buffer
中的內容有一部分是未定義的... 所以為了防止這種情況我又添加了一個變數, 叫做num
, 它代表的是目前buffer
中實際有效的字元數.
只有幾個地方進行了修改, 很簡單 :
Lexer(std::ifstream& ifs):ifs(ifs), EndOfFile(false), idx(0), row(1), column(0){
updateBuffer();
lex();
}
void updateBuffer(){
// first read...
if(num == 0 && !EndOfFile){
ifs.read(buffer, BUFFERSIZE);
num = ifs.gcount();
if(ifs.eof()){
EndOfFile = true;
}
return;
}
if(idx <= LIMITSIZE || EndOfFile){
return;
}
idx -= LIMITSIZE;
strncpy(buffer, buffer + LIMITSIZE, COPYLENGTH);
ifs.read(buffer + COPYLENGTH, LIMITSIZE);
num = COPYLENGTH + ifs.gcount();
if(ifs.eof()){
EndOfFile = true;
}
}
void Lexer::eatSpace(){
char ch = 0;
//change here!!!
while(idx != num && (ch = buffer[idx++])){
updateBuffer();
switch (ch){
case '\n':{
++row;
column = 0;
break;
}
case ' ':{
}
case '\t':{
++column;
break;
}
default:{
--idx;
return;
}
}
}
}
char getNextChar(){
updateBuffer();
++column;
//change here!!!
if(idx == num){
// error.
}
return buffer[idx++];
}
所以改動的地方都已經使用備註標出, 主要思路在於 :
- 增加了
updateBuffer
初始化buffer
功能, 所以關於緩衝中有效字元數量(num), 是否達到文件末尾(EndOfFile)等變數的設置都會在這個函數, 也只會這個函數中進行. - 源代碼的結束只出現在
eatSpace()
中和每一次迴圈的開頭, 所以在getNextChar()
中出現只有一種情況, 就是源代碼有錯.
lex()的設計
最後的任務就是設計這個關鍵的函數lex()
, 我個人想到的最清晰易懂的方式就是通過預讀然後調用相應的識別函數的方式來進行詞法解析.
我們從簡單的幾個入手 :
void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + "=", row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
}
case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, ch + ch + "", row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
}
case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
break;
case ';':
list.pushBack(Token(Token::SEMI, ch + "", row, column));
break;
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + "", row, column));
break;
}
eatSpace();
}
}
然後是字元串解析函數 :
std::string Lexer::stringParse() {
char ch;
std::string temp;
bool escape = false;
while(escape || (ch = getNextChar()) != '"'){
if(escape){
switch (ch){
case 'n':
temp += '\n';
break;
case 't':
temp += '\t';
break;
case '"':
temp += '\"';
break;
case '\\':
temp += '\\';
break;
default:
//error
;
}
escape = false;
continue;
}
switch (ch){
case '\\':
escape = true;
continue;
default:
temp += ch;
}
}
return temp;
}
此時的lexer()
...
void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + "=", row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
}
case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, ch + ch + "", row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
}
case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
break;
case ';':
list.pushBack(Token(Token::SEMI, ch + "", row, column));
break;
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + "", row, column));
break;
case '0': {
// int or float
std::string temp("0");
if (getNextChar() == '.') {
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
list.pushBack(Token(Token::INT, temp, row, column));
break;
}
case '"':
list.pushBack(Token(Token::STRING, stringParse(), row, column));
}
eatSpace();
}
}
由於int
和float
有兩種情況, 所以這裡分開設計, 先討論了比較簡單的0
開頭的情況.
然後把剩下的一些在switch的default中補齊.
void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + std::string("="), row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
}
break;
}
case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, std::string("") + ch + ch , row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
}
break;
}
case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
break;
case ';':
list.pushBack(Token(Token::SEMI, ch + std::string(""), row, column));
break;
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + std::string(""), row, column));
break;
case '0': {
// int or float
bool isFloat = false;
std::string temp("0");
if (getNextChar() == '.') {
isFloat = true;
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
isFloat ? list.pushBack(Token(Token::FLOAT, temp, row, column))
: list.pushBack(Token(Token::INT, temp, row, column));
break;
}
case '"':
list.pushBack(Token(Token::STRING, stringParse(), row, column));
default:{
if(isDigit(ch) && ch != 0){
bool isFloat = false;
std::string temp;
temp += ch;
while (isDigit(ch = getNextChar())) {
temp += ch;
}
if (getNextChar() == '.') {
isFloat = true;
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
isFloat ? list.pushBack(Token(Token::FLOAT, temp, row, column))
: list.pushBack(Token(Token::INT, temp, row, column));
}
else if(isID(ch) && !isDigit(ch)){
std::string temp;
temp += ch;
while (isID(ch = getNextChar())) {
temp += ch;
}
backtrace();
list.pushBack(Token(Token::IDENTIFIER, temp, row, column));
}
else{
//error
}
}
}
eatSpace();
}
}
然後在進行了簡單的測試 :
main.cpp :
#include <iostream>
#include <fstream>
#include "Font/Lexer/Lexer.h"
int main() {
std::ifstream ifstream("/Users/zhangzhimin/x.txt");
Lexer lexer(ifstream);
lexer.print();
return 0;
}
x.txt
int main(){
int x = 3;
x += 3.5;
a123b = 4.3333;
"\n1\t2\\\"";
> *= +++ <
return 0;
}
結果如下
int
main
(
)
{
int
x
=
3
x
+=
3
5
a123b
=
4
3333
1 2\"
;
*=
+
+
+
return
0
;
}
除了沒有加入報錯, 其他的都ok了, 我目前還不太瞭解C++中的異常機制, 畢竟才學了不到一個月, 其他的以後再說吧, 反正詞法分析就告一段落了...