·字符集合:一个范围的列表
·串联:子树列表
·并联:子树列表
·重复:最小次数,最大次数,是否无限,是否贪婪,子树
·左边界
·右边界
·功能: 子树
功能(正常、匿名捕获、正向预查、反向预查)
描述(表达式命名、表达式引用、命名捕获、命名检查)
名字
我自己对比小v的原文稍微改动了一点,在重复后多加了子树,和是否贪婪,那个子树他是写漏了,是否贪婪,我不清楚他是怎么想的。转换成C语言的结构就是:
struct reg_node_
{
enum
{
char_class, // 字符集合
concat, // 串联
alter, // 并联
repetition, // 重复
lbound, // 左边界
rbound, // 右边界
func // 功能
} type;
union
{
vector char_class; // vector元素类型:struct { begin, end } range
vector concat; // vector元素类型:struct reg_node_;
vector alter; // vector元素类型:struct reg_node_;
struct
{
reg_node child_tree; // 子树
uint16 min_times; // 最小重复次数
uint16 max_times; // 最大重复次数
bool is_infinite; // 是否无限
bool is_greed;
} repetition;
struct
{
reg_node child_tree; // 子树
enum
{
normal, // 正常
anonymous, // 匿名捕获
positive, // 正向预查
negative // 逆向预查
} action;
enum
{
expression_name, // 表达式命名
expression_refer, // 表达式引用
name_trap, // 命名捕获
name_check // 命名检查
} descrip;
String name; // 名字
} func;
} data;
};
大概就是上面这样没错了。这里我讲下为什么把正则表达式转换成树,再转换成ENFA的好处,我的感觉是非常有利于数据之间的传递。让数据的格式变的非常的标准,而有利于模块化。你假如试过没有用中间这个树就直接根据正则表达式转换ENFA的经验,那么感触会更加的真实点……,我就写过,噩梦的日子~~~。
写到这里真的不知道该怎么写了,小v的文章真的解释的很清楚了。我讲点自己的看法吧。
1. 非常鄙视文章中那句,回想一下我们小时候……
2. 不推荐使用
·消除左递归
·递归下降分析法
因为我感觉这需要了解编译器的不少知识。而且标准的中缀到后缀的转换已经够用了。不该节外生枝,但假如你做过编译器那么我前面的反对无效。
3. 在
struct Status
{
vector<Edge*> InEdges;
vector<Edge*> OutEdges;
bool FinalStatus;
};
我在这里没有使用bool FinalStatus,而是改成了String FinalStatus,在声明了全局变量
String g_FinalStatus = L"FinalStatus";
之所以这样做,是因为我不想在多花4个字节的空间,假如状态不是终结状态,那么FinalStatus指向的应该是NULL,否则就是终结状态,另外这种办法在做词法分析的时候有不少的好处(我自以为),比较容易处理多个终结状态,和规则优先的问题。
4. 貌似又一处笔误的问题,那就是Edge结构中XXX描述的该是字符范围,另外的YYY该用来描述功能边。
5. 另外就是关于求字符集合的问题也该不难,又有错别字。大家找到那一句 -- 然后我们要构造集合Bi使得……,该是Bj。 字符集合我使用了链表来实现,通过不停的比对,找到Bj的最少解。
6. 接下去也就没什么问题了,扩展正则表达式不难,因为小v的算法已经给了出来了。我发现的不少问题又都是跟数据结构有关的。我个人感觉到了最后,时间的花费主要都消耗在了函数调用上。所以大家看下LINUX内核中关于链表的使用,把用的较多的函数改成inline。另外迭代器这个概念是我突然想通的,但是用到了很多的类型转换。幸亏整型之间的转换不比,整形和浮点型的转换……。