来张着色效果图先:
它已经按照规则识别出了:
字符串,标识符,数字.
因为没有写 多行字符串,续行符,#预处理命令等规则,所以它看起来略微混乱.文字乱码是因为输出环境是ANSI…
简易原理:
- 定义不同规则的正则表达式
- 对待处理的文本进行正则匹配,捕获匹配的部分.至到待匹配文本结束.
- 如果没有匹配就是词法错误
- 如果有匹配就将捕获到的片段填入Token中
- 针对前文生成的Token流进行类型遍历和输出
- 将Token文本以不同的颜色输出
没了。。。
- 将Token文本以不同的颜色输出
原理
两周自制脚本语言:
Lex(词法分析)使用正则来做.
- 用一个正则匹配所有可能的情况:
- 空格换行
- 注释
- 数字
- 字符串
- 标志符
- 通过命中的匹配.将其类型和值保存.
- 对该集合进行遍历.渲染相应的颜色.
习得
这次算是我第一次正规完成Lex.效果很喜人.
编译器,就是这样:数据->token流->tree->遍历树做想做的事情->生成目标
这次打通了token流,能实现 颜色渲染.
展望
我曾经遇到的困难是:
- 多种可能的分析
这涉及到优先级问题和向前看问题.
不足
正则还是太复杂了.一大堆…
可以设计成多个表达式的拼接.
目前没有实现 多个标志符的匹配,以及对称内容的匹配:<> /**/
代码如下:
#include <iostream>
#include <string>
#include <regex>
#include <vector>
class TOKEN
{
public:
std::string valueGet() {
return value;
}
void valueSet(std::string str) {
value = str;
}
std::string typeGet() {
return type;
}
void typeSet(std::string str) {
type = str;
}
private:
std::string value;
std::string type;
};
#include <windows.h>
void SetColor(unsigned short forecolor = 4, unsigned short backgroudcolor = 0)
{
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); //获取缓冲区句柄
SetConsoleTextAttribute(hCon, forecolor | backgroudcolor); //设置文本及背景色
}
std::vector<TOKEN*> Lex(std::string s) {
std::vector<TOKEN*> m_token;
std::smatch m;
//|\\p{Punct} !|\"|#|\\$|%|&|'|\\(|\\)|\\*|\\+|,|-|\\.|/|:|;|<|=|>|\\?|@|\\[|\\\|\\]|\\^|_|`|\\{|\\||\\}|~
std::regex e("(\\s+)|((//.*)|([0-9]+)|(\"(\\\\\"|\\\\|\\\\n|[^\"])*\")|([A-Z_a-z][A-Z_a-z0-9]*|==|<=|>=|&&|\\/|\\*|\\-|\\+|\\(|\\)|>|<|=|%|&|;|\\{|\\}|!|\"|#|\\$|%|&|'|\\(|\\)|\\*|\\+|,|-|\\.|/|:|;|<|=|>|\\?|@|\\[|\\|\\]|\\^|_|`|\\{|\\||\\}|~))?"); // matches words beginning by "sub"
while (std::regex_search(s, m, e)) {
if (m[1].length())
{
TOKEN* p = new TOKEN();
p->valueSet(m[1].str());
p->typeSet("空格换行");
m_token.push_back(p);
}
else if (m[3].length()) {
TOKEN* p = new TOKEN();
p->valueSet(m[3].str());
p->typeSet("注释");
m_token.push_back(p);
}
else if (m[4].length()) {
TOKEN* p = new TOKEN();
p->valueSet(m[4].str());
p->typeSet("数字");
m_token.push_back(p);
}
else if (m[5].length()) {
TOKEN* p = new TOKEN();
p->valueSet(m[5].str());
p->typeSet("字符串");
m_token.push_back(p);
}
else if (m[7].length()) {
TOKEN* p = new TOKEN();
p->valueSet(m[7].str());
p->typeSet("标识符");
m_token.push_back(p);
}
s = m.suffix().str();
if (s.empty())break;
if (0 == m[0].length()) {
std::cout << "发生无法识别的类型.从下一行开始识别" << std::endl;
auto i=s.find('\n');
if (i != std::string::npos) {
s = s.substr(i);
if (s.length() > 1) {
s=s.substr(1);
}
else {
break;
}
}
else {
break;
}
}
}
return m_token;
}
#include <string>
#include <fstream>
#include <sstream>
#define 黑背景 0
#define 亮白背景 BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
#define 白色前景 FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
#define 亮蓝前景 FOREGROUND_BLUE | FOREGROUND_INTENSITY
#define 蓝色前景 FOREGROUND_BLUE
#define 棕色前景 FOREGROUND_RED | FOREGROUND_GREEN
#define 绿色前景 FOREGROUND_GREEN
int main()
{
std::string s = "function abc() {\r\n"\
" if (a | b)//zhu shi\r\n"\
" printf(\"ok\");\r\n"\
" }\r\n"\
" \r\n";
std::ifstream t("file2.cpp");
std::stringstream buffer;
buffer << t.rdbuf();
std::string contents(buffer.str());
auto m_token = Lex(contents);
/*
剩下的工作是: 把不同位置的字符串读出来,再按想法做处理
*/
std::string key_words_text = "if else for while { } ( ) ; == != ! | bool int float double auto ? : > = < * + - * / \\ . break continue return new delete try const static";
std::istringstream iss(key_words_text);
std::vector<std::string> key_words(std::istream_iterator<std::string>{iss},
std::istream_iterator<std::string>());
for (auto t : m_token) {
if (t->typeGet() == "空格换行") {
SetColor(白色前景, 亮白背景);
}
else if (t->typeGet() == "注释") {
SetColor(绿色前景, 亮白背景);
}
else if (t->typeGet() == "数字") {
SetColor(FOREGROUND_RED|FOREGROUND_INTENSITY, 亮白背景);
}
else if (t->typeGet() == "字符串") {
SetColor(棕色前景, 亮白背景);
}
else if (t->typeGet() == "标识符") {
/*
如果是关键字里的东西 则 显示 亮蓝
*/
bool b = false;
for (auto tmp : key_words) {
if (tmp == t->valueGet()) {
b = true;
SetColor(亮蓝前景, 亮白背景);
}
}
if (!b) {
SetColor(绿色前景, 亮白背景);
}
}
std::cout << t->valueGet();
}
return 0;
}