2025年java+ebnf解析_用lpeg解析文本语法

java+ebnf解析_用lpeg解析文本语法所谓语法解析 就是将文本中符合既定规则的子串提取出来 欲解析 先要找出文本的既定规则 欲写出代码 先要将规则从抽象域转为自然语言的形式域 就像人们把数学概念用符号固化下来 这种符号 先辈们早已给出了方案 最常用的是 BNF lpeg 便是自然地契合 BNF 的

大家好,我是讯享网,很高兴认识大家。

所谓语法解析,就是将文本中符合既定规则的子串提取出来。欲解析,先要找出文本的既定规则;欲写出代码,先要将规则从抽象域转为自然语言的形式域,就像人们把数学概念用符号固化下来。这种符号,先辈们早已给出了方案,最常用的是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 ::= " " | "\t"

newline ::= "\r\n" | "\n"

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 ::= " " | "\t"

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"\t"

local newline = P"\r\n" + "\n"

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

小讯
上一篇 2025-04-07 17:53
下一篇 2025-01-27 19:59

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/72195.html