-
Notifications
You must be signed in to change notification settings - Fork 2
/
scanner.h
115 lines (104 loc) · 2.79 KB
/
scanner.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#pragma once
#include "error.h"
#include <memory>
#include <string>
namespace akan {
class Scanner {
static void CloseFile(std::FILE *fp) {
if (fp) {
std::fclose(fp);
}
}
// File
const char *file_name_ = nullptr;
std::unique_ptr<FILE, decltype(CloseFile) *> file_;
// Characters read
static constexpr int buf_len_ = 80; // Length of scan buffer
char line_[buf_len_];
// Read status
int line_len_ = 0; // Length of current line
int read_pos_ = -1; // Read position
char last_ch_ = 0; // Last character, used to judge the line break position
int line_num_ = 1; // Row Number
int col_num_ = 0; // Column Number
// Debug helper
static std::string ShowChar(char ch) {
char s[16];
switch (ch) {
case -1:
std::sprintf(s, "%s <%d>", "EOF", ch);
break;
case '\n':
std::sprintf(s, "%s <%d>", "\\n", ch);
break;
case '\t':
std::sprintf(s, "%s <%d>", "\\t", ch);
break;
case ' ':
std::sprintf(s, "%s <%d>", "blank", ch);
break;
default:
std::sprintf(s, "%c <%d>", ch, ch);
}
return std::string(s);
}
public:
Scanner(const char *name)
: file_name_(name), file_(std::fopen(name, "r"), &CloseFile) {
if (!file_) {
PrintCommonError(
FATAL, "Fail to open the file %s! Please check filename and path.\n",
name);
Error::IncrErrorNum();
}
}
Scanner(const Scanner &) = delete;
Scanner &operator=(const Scanner &) = delete;
~Scanner() = default;
// Scan characters from buffer
int Scan() {
if (!file_)
return -1;
if (read_pos_ == line_len_ - 1) {
line_len_ = fread(line_, 1, buf_len_, file_.get()); // reload buffer data
if (line_len_ == 0) { // no data
// indicate end of file
line_len_ = 1;
line_[0] = -1;
last_ch_ = -1;
return -1;
}
read_pos_ = -1; // restore reading position
}
++read_pos_;
char ch = line_[read_pos_]; // get the new char
if (last_ch_ == '\n') { // start new line
++line_num_;
col_num_ = 0;
} else {
++col_num_;
}
last_ch_ = ch;
return ch;
}
// Getter
const char *GetFile() const { return file_name_; }
int GetLine() const { return line_num_; }
int GetCol() const { return col_num_; }
private:
static void TestImpl(const char *file_name) {
Scanner scanner(file_name);
char ch;
do {
ch = scanner.Scan();
std::printf("%8s\tline: %3d\tcol: %3d\n", ShowChar(ch).c_str(),
scanner.GetLine(), scanner.GetCol());
} while (ch != -1);
std::printf("Finish the scan for %s\n", file_name);
}
public:
static void MainTest(int argc = 0, char *argv[] = nullptr) {
TestImpl("file/arithmetic.c");
}
};
} // namespace akan