752 lines
20 KiB
C++
752 lines
20 KiB
C++
|
#include <cstdio>
|
||
|
#include <list>
|
||
|
#include <unordered_map>
|
||
|
#include <unordered_set>
|
||
|
#include <vector>
|
||
|
#include <string>
|
||
|
|
||
|
class LexicalParser
|
||
|
{
|
||
|
public:
|
||
|
int LineCount = 0;
|
||
|
int KeywordCount = 0;
|
||
|
int IdentifierCount = 0;
|
||
|
int OperatorCount = 0;
|
||
|
int DelimiterCount = 0;
|
||
|
int CharCount = 0;
|
||
|
int StringCount = 0;
|
||
|
int NumberCount = 0;
|
||
|
int ErrorCount = 0;
|
||
|
|
||
|
explicit LexicalParser(FILE *file)
|
||
|
{
|
||
|
this->File = file;
|
||
|
}
|
||
|
|
||
|
void Loop()
|
||
|
{
|
||
|
bool mark = false;
|
||
|
|
||
|
while (Readline())
|
||
|
{
|
||
|
while (!Buffer.empty())
|
||
|
{
|
||
|
if (mark)
|
||
|
{
|
||
|
// 多行注释
|
||
|
for (auto i = Buffer.begin(); i != Buffer.end(); i++)
|
||
|
{
|
||
|
if (*i == '*')
|
||
|
{
|
||
|
i++;
|
||
|
if (i != Buffer.end() and *i == '/')
|
||
|
{
|
||
|
mark = false;
|
||
|
|
||
|
i++;
|
||
|
if (i == Buffer.end())
|
||
|
{
|
||
|
Buffer.clear();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Buffer.erase(Buffer.begin(), i);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mark)
|
||
|
{
|
||
|
// 说明在这行没有找到结束符
|
||
|
Buffer.clear();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// 这行读取完成了
|
||
|
if (Buffer.empty())
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
if (*pos == '/')
|
||
|
{
|
||
|
pos++;
|
||
|
if (pos != Buffer.end())
|
||
|
{
|
||
|
// 判断单行注释
|
||
|
if (*pos == '/')
|
||
|
{
|
||
|
Buffer.clear();
|
||
|
continue;
|
||
|
}
|
||
|
else if (*pos == '*')
|
||
|
{
|
||
|
mark = true;
|
||
|
pos++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*Buffer.begin() == ' ' or *Buffer.begin() == '\t')
|
||
|
{
|
||
|
Buffer.pop_front();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// 处理特殊错误 @
|
||
|
if (*Buffer.begin() == '@')
|
||
|
{
|
||
|
Buffer.pop_front();
|
||
|
|
||
|
ErrorCount++;
|
||
|
printf("%d <ERROR,@>\n", LineCount);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!Parse())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
std::list<char> Buffer;
|
||
|
FILE *File;
|
||
|
|
||
|
bool Parse()
|
||
|
{
|
||
|
if (ParseCharacter() or ParseString())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (ParseNumber())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (ParseOperator() or ParseDelimiter())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (ParseKeyword())
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return ParseIdentifier();
|
||
|
}
|
||
|
|
||
|
bool Readline()
|
||
|
{
|
||
|
// 标记是否是最后一行
|
||
|
bool read = false;
|
||
|
while (true)
|
||
|
{
|
||
|
int c = fgetc(File);
|
||
|
|
||
|
if (c == EOF)
|
||
|
{
|
||
|
if (read)
|
||
|
{
|
||
|
LineCount++;
|
||
|
}
|
||
|
return read;
|
||
|
}
|
||
|
else if (c == '\n')
|
||
|
{
|
||
|
LineCount++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Buffer.emplace_back((char) c);
|
||
|
read = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ParseKeyword()
|
||
|
{
|
||
|
auto begin = Buffer.begin();
|
||
|
|
||
|
if (KeywordsMap.count(*begin) != 0)
|
||
|
{
|
||
|
const auto &array = KeywordsMap.at(*begin);
|
||
|
|
||
|
|
||
|
for (const auto &i: array)
|
||
|
{
|
||
|
if (i.length() > Buffer.size())
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
bool flag = true;
|
||
|
for (auto c: i)
|
||
|
{
|
||
|
if (c != *pos)
|
||
|
{
|
||
|
flag = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (flag)
|
||
|
{
|
||
|
// 同标识符吻合的字符串
|
||
|
// 如果是标识符,应该是分隔符或者空格
|
||
|
if (pos == Buffer.end() or *pos == ' ' or DelimitersSet.count(*pos) != 0
|
||
|
or OperatorsMap.count(*pos) != 0)
|
||
|
{
|
||
|
KeywordCount++;
|
||
|
|
||
|
std::string output;
|
||
|
for (auto j = Buffer.begin(); j != pos; j++)
|
||
|
{
|
||
|
output += *j;
|
||
|
}
|
||
|
|
||
|
printf("%d <KEYWORD,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseIdentifier()
|
||
|
{
|
||
|
auto pos = Buffer.begin();
|
||
|
|
||
|
if (*pos == '_' or (*pos >= 'A' and *pos <= 'Z') or (*pos >= 'a' and *pos <= 'z'))
|
||
|
{
|
||
|
while (*pos == '_' or (*pos >= 'A' and *pos <= 'Z') or (*pos >= 'a' and *pos <= 'z')
|
||
|
or (*pos >= '0' and *pos <= '9'))
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (pos == Buffer.end() or *pos == ' ' or DelimitersSet.count(*pos) != 0
|
||
|
or OperatorsMap.count(*pos) != 0)
|
||
|
{
|
||
|
IdentifierCount++;
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <IDENTIFIER,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseDelimiter()
|
||
|
{
|
||
|
auto pos = Buffer.begin();
|
||
|
|
||
|
if (DelimitersSet.count(*pos) != 0)
|
||
|
{
|
||
|
DelimiterCount++;
|
||
|
printf("%d <DELIMITER,%c>\n", LineCount, *pos);
|
||
|
|
||
|
Buffer.pop_front();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseOperator()
|
||
|
{
|
||
|
auto begin = Buffer.begin();
|
||
|
|
||
|
if (OperatorsMap.count(*begin))
|
||
|
{
|
||
|
const auto &array = OperatorsMap.at(*begin);
|
||
|
|
||
|
for (const auto &s: array)
|
||
|
{
|
||
|
if (s.length() > Buffer.size())
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
bool flag = true;
|
||
|
|
||
|
for (auto i: s)
|
||
|
{
|
||
|
if (i != *pos)
|
||
|
{
|
||
|
flag = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (flag)
|
||
|
{
|
||
|
OperatorCount++;
|
||
|
// 感觉,,,,
|
||
|
// 可以不用判断运算符的后面是什么
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <OPERATOR,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseCharacter()
|
||
|
{
|
||
|
std::string output;
|
||
|
|
||
|
auto first = Buffer.begin();
|
||
|
auto second = first;
|
||
|
second++;
|
||
|
|
||
|
if (*first == 'L' or *first == 'u' or *first == 'U')
|
||
|
{
|
||
|
if (*second == '\'')
|
||
|
{
|
||
|
output += *first;
|
||
|
Buffer.erase(first, second);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
|
||
|
if (*pos == '\'')
|
||
|
{
|
||
|
pos++;
|
||
|
while (true)
|
||
|
{
|
||
|
//处理本行没有闭合的错误
|
||
|
if (pos == Buffer.end())
|
||
|
{
|
||
|
for (auto c: Buffer)
|
||
|
{
|
||
|
output += c;
|
||
|
}
|
||
|
|
||
|
Buffer.clear();
|
||
|
ErrorCount++;
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (*pos == '\'')
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (*pos == '\\')
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
pos++;
|
||
|
}
|
||
|
pos++;
|
||
|
|
||
|
CharCount++;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <CHARCON,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseString()
|
||
|
{
|
||
|
auto first = Buffer.begin();
|
||
|
auto second = first;
|
||
|
second++;
|
||
|
|
||
|
std::string output;
|
||
|
|
||
|
if (*first == 'u' or *first == 'U' or *first == 'L')
|
||
|
{
|
||
|
auto third = second;
|
||
|
third++;
|
||
|
if (*second == '\"')
|
||
|
{
|
||
|
output += *first;
|
||
|
Buffer.erase(first, second);
|
||
|
}
|
||
|
else if (*first == 'u' and *second == '8')
|
||
|
{
|
||
|
if (*third == '\"')
|
||
|
{
|
||
|
output = "u8";
|
||
|
Buffer.erase(first, third);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
|
||
|
if (*pos == '"')
|
||
|
{
|
||
|
pos++;
|
||
|
while (true)
|
||
|
{
|
||
|
//处理本行没有闭合的错误
|
||
|
if (pos == Buffer.end())
|
||
|
{
|
||
|
for (auto c: Buffer)
|
||
|
{
|
||
|
output += c;
|
||
|
}
|
||
|
|
||
|
Buffer.clear();
|
||
|
ErrorCount++;
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (*pos == '"')
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (*pos == '\\')
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
pos++;
|
||
|
}
|
||
|
pos++;
|
||
|
|
||
|
StringCount++;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <STRING,%s>\n", LineCount, output.c_str());
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ParseNumber()
|
||
|
{
|
||
|
auto first = Buffer.begin();
|
||
|
auto second = first;
|
||
|
second++;
|
||
|
auto third = second;
|
||
|
third++;
|
||
|
|
||
|
if ((*first >= '0' and *first <= '9') or *first == '.')
|
||
|
{
|
||
|
if (*first == '0' and (*second == 'x' or *second == 'X'))
|
||
|
{
|
||
|
// 处理十六进制数据
|
||
|
ParseHexadecimalNumber();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
auto pos = Buffer.begin();
|
||
|
if (*first == '.')
|
||
|
{
|
||
|
// 区分小数点和访问符
|
||
|
if (*second < '0' or *second > '9')
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
while (pos != Buffer.end() and *pos >= '0' and *pos <= '9' or *pos == '.')
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (pos != Buffer.end() and (*pos == 'e' or *pos == 'E' or *pos == '.'))
|
||
|
{
|
||
|
pos++;
|
||
|
|
||
|
if (*pos == '+' or *pos == '-')
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (pos == Buffer.end() or *pos < '0' or *pos > '9')
|
||
|
{
|
||
|
// 坏了
|
||
|
while (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
ErrorCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (pos != Buffer.end() and *pos >= '0' and *pos <= '9' or *pos == '.')
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
std::unordered_set<char> suffixSet = {'u', 'l', 'U', 'L', 'f', 'F'};
|
||
|
|
||
|
if (pos != Buffer.end() and suffixSet.count(*pos) != 0)
|
||
|
{
|
||
|
while (pos != Buffer.end() and suffixSet.count(*pos) != 0)
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
if (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
while (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
ErrorCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <NUMBER,%s>\n", LineCount, output.c_str());
|
||
|
NumberCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
else if (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
while (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
ErrorCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <NUMBER,%s>\n", LineCount, output.c_str());
|
||
|
NumberCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void ParseHexadecimalNumber()
|
||
|
{
|
||
|
auto pos = Buffer.begin();
|
||
|
pos++;
|
||
|
pos++;
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
if ((*pos >= '0' and *pos <= '9') or
|
||
|
(*pos >= 'A' and *pos <= 'F') or
|
||
|
(*pos >= 'a' and *pos <= 'f'))
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
else if (pos == Buffer.end() or *pos == ' ' or *pos == '\t' or
|
||
|
OperatorsMap.count(*pos) != 0 or
|
||
|
DelimitersSet.count(*pos) != 0)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// 遇到错误
|
||
|
while (pos != Buffer.end() and *pos != ' ' and *pos != '\t' and
|
||
|
OperatorsMap.count(*pos) == 0 and
|
||
|
DelimitersSet.count(*pos) == 0)
|
||
|
{
|
||
|
pos++;
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <ERROR,%s>\n", LineCount, output.c_str());
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
ErrorCount++;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::string output;
|
||
|
for (auto i = Buffer.begin(); i != pos; i++)
|
||
|
{
|
||
|
output += *i;
|
||
|
}
|
||
|
|
||
|
printf("%d <NUMBER,%s>\n", LineCount, output.c_str());
|
||
|
NumberCount++;
|
||
|
Buffer.erase(Buffer.begin(), pos);
|
||
|
}
|
||
|
|
||
|
const std::unordered_map<char, std::vector<std::string>> KeywordsMap = {
|
||
|
{'a', {"auto"}},
|
||
|
{'b', {"break"}},
|
||
|
{'c', {"case", "char", "const", "continue"}},
|
||
|
{'d', {"double", "default", "do"}},
|
||
|
{'e', {"else", "extern", "enum"}},
|
||
|
{'f', {"float", "for"}},
|
||
|
{'g', {"goto"}},
|
||
|
{'i', {"if", "int"}},
|
||
|
{'l', {"long"}},
|
||
|
{'s', {"struct", "static", "switch", "short", "signed", "sizeof"}},
|
||
|
{'r', {"register", "return"}},
|
||
|
{'t', {"typedef",}},
|
||
|
{'u', {"union", "unsigned"}},
|
||
|
{'v', {"void", "volatile"}},
|
||
|
{'w', {"while"}}
|
||
|
};
|
||
|
|
||
|
const std::unordered_map<char, std::vector<std::string>> OperatorsMap = {
|
||
|
{'+', {"++", "+=", "+"}},
|
||
|
{'-', {"--", "-=", "->", "-"}},
|
||
|
{'*', {"*=", "*"}},
|
||
|
{'/', {"/=", "/"}},
|
||
|
{'%', {"%=", "%"}},
|
||
|
{'=', {"==", "="}},
|
||
|
{'!', {"!=", "!"}},
|
||
|
{'>', {">>=", ">>", ">=", ">"}},
|
||
|
{'<', {"<<=", "<<", "<=", "<"}},
|
||
|
{'&', {"&&", "&=", "&"}},
|
||
|
{'|', {"||", "|=", "|"}},
|
||
|
{'^', {"^=", "^"}},
|
||
|
{'.', {"."}},
|
||
|
{'~', {"~"}}
|
||
|
};
|
||
|
|
||
|
const std::unordered_set<char> DelimitersSet = {
|
||
|
';', ',', ':', '?', '(', ')', '[', ']', '{', '}'
|
||
|
};
|
||
|
};
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
FILE *source_file = fopen(argv[1], "r");
|
||
|
|
||
|
if (source_file == nullptr || argc != 2)
|
||
|
{
|
||
|
printf("Failed to open source File.\n");
|
||
|
}
|
||
|
|
||
|
LexicalParser parser(source_file);
|
||
|
|
||
|
parser.Loop();
|
||
|
|
||
|
printf("%d\n", parser.LineCount);
|
||
|
printf("%d %d %d %d %d %d %d\n", parser.KeywordCount,
|
||
|
parser.IdentifierCount,
|
||
|
parser.OperatorCount,
|
||
|
parser.DelimiterCount,
|
||
|
parser.CharCount,
|
||
|
parser.StringCount,
|
||
|
parser.NumberCount);
|
||
|
printf("%d", parser.ErrorCount);
|
||
|
|
||
|
fclose(source_file);
|
||
|
return 0;
|
||
|
}
|