• 用lpeg解析文本语法


    所谓语法解析,就是将文本中符合既定规则的子串提取出来。欲解析,先要找出文本的既定规则;欲写出代码,先要将规则从抽象域转为自然语言的形式域,就像人们把数学概念用符号固化下来。这种符号,先辈们早已给出了方案,最常用的是BNF。lpeg便是自然地契合BNF的,这也是它与正则表达式等模式匹配库最大的不同,也是它最大的优势,它把每个模式对象作为lua的第一类对象,也就是可以存储于变量中,模式对象间可以相互运算,用BNF来看,每个模式对象便是非终结符,其定义便是终结符。如匹配一个email:

    • 用正则表达式可以为:
    ^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$
    
    • 用lpeg可以为:
    local lpeg = require "lpeg"
    local R = lpeg.R
    local C = lpeg.C
    local alpha = R"az" + R"AZ" +  R"_"
    local num = R"09"
    local world = alpha + num
    local email = world^1 * "@" * world^1 * "." * world^1
    

    lpeg看起来不更清晰,更接近自然语言些吗?

    下面用一个复杂些的例子来说明lpeg的运用,来解析C语言的结构体定义,然后将提取的类型定义保存在一张表中。这张表为一个数组,每个元素的结构如下:

    --[[
    struct student{
    	char *name;
    	int age;
    };
    
    --]]
    {
    	name = student
    	fields = {
    		[1] = {"char","*","name"},
    		[2] = {"int","age"}
    	}
    }
    

    我们用的文本如下:

    //学生
    struct student{
    	char *name;
    	int age;
    };
    
    //账号
    struct account{
    	char *user;
    	char pwd[32];//md5
    };
    

    第一步,把文本的规则形式化,用EBNF描述出来。
    struct ::= banks name "{" banks {field} banks"};" banks
    space ::= " " | " "
    newline ::= " " | " "
    name ::= alpha | "" {world}
    world = alpha | num | "
    "
    alpha ::= lower | upper
    lower ::= "a"|"b"|"c"|"d"|"e"|"f"|"g"|"h"|"i"|"j"|"k"|"l"|"m"|"n"|"o"|"p"|"q"|"r"|"s"|"t"|"u"|"v"|"w"|"x"|"y"|"z"
    num ::= "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
    upper ::= 同lower的大写字母形式
    bank ::= newline | comment | space
    banks ::= {bank}
    comment ::= "//" {.}
    field ::= type space | {space} ptr {space} name [array] ; banks"
    space ::= " " | " "
    type ::= name
    ptr ::= {"*"}
    array ::= "[" {num} "]"

    第二步,把EBNF转为lpeg模式

    local lpeg = require "lpeg"
    local P = lpeg.P
    local S = lpeg.S
    local R = lpeg.R
    local C = lpeg.C
    local Ct = lpeg.Ct
    local Cg = lpeg.Cg
    local Cc = lpeg.Cc
    local V = lpeg.V
    local Carg = lpeg.Carg
    local Cmt = lpeg.Cmt
    
    local alpha = R"az" + R"AZ" + "_"
    local num = R"09"
    local space = P" " + P"	"
    local newline = P"
    " + "
    "
    local comment = P"//" * (1 - newline)^0   
    local bank = space + newline + comment
    local banks = bank^0
    local world = alpha + num + "_"
    local name = C(alpha * world^1)
    local dtype = name 
    local array = C(P"[" * num^0 * "]")
    local ptr = C(P"*")
    
    local function multipat(...)
    	local pat = P" "^0
    	local pp = {...}
    	for _,v in ipairs(pp) do
    		pat = pat * v
    	end
    	return Ct(pat)
    end
    
    local field = multipat(banks * dtype * space^0 * ptr^0 * space^0 * name * array^0 * ";" * banks)
    local struct = multipat(banks * "struct" * space^0 * Cg(name,"name") * "{" * banks * Cg(Ct(field^0),"fields") * banks * "};" * banks)
    local typedef = Ct(struct^0)
    
    --解析
    local r = typedef:match(text)
    

    用lpeg,要以BNF为指导,然后根据保存形式选择合适的捕获。它虽然很容易提取符合语法的子串,但无法检测子串的合法性,如字段的数据类型是否有效,其合法性就需要另外检测了。

  • 相关阅读:
    数梦工场:新思维、新技术下的互联网+政务
    计算成就价值_数据实现梦想——达科在DT时代转型历程的分享
    AliSQL开源功能特性
    mysql 索引的使用
    sql经典面试题
    数据库理论知识点
    sql语句面试练习
    数据库范式的选择使用
    sql常用语句
    数据库范式
  • 原文地址:https://www.cnblogs.com/watercoldyi/p/6799958.html
Copyright © 2020-2023  润新知