如何使用Pyparsing解析嵌套函数调用?

3 投票
1 回答
1192 浏览
提问于 2025-04-18 04:31

我正在开发一个CSS解析器,但我不知道怎么解析像 alpha(rgb(1, 2, 3), 0.5) 这样的嵌套函数调用。

这是我的代码:

# -*- coding: utf-8 -*-


from pyparsing import * #, Word, alphas, OneOrMore, countedArray, And, srange, hexnums,  Combine, cStyleComment

from string import punctuation
from app.utils.file_manager import loadCSSFile, writeFile
from pyparsing import OneOrMore, Optional, Word, quotedString, delimitedList,  Suppress
from __builtin__ import len

# divide tudo por espaços, tabulações e novas linhas
words = ZeroOrMore(cStyleComment | Word(printables))

digit = '0123456789'; underscore = '_'; hyphen =  '-'
hexDigit = 'abcdefABCDEF' + digit
letter = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
az_AZ_underscore = underscore + letter;  
az_AZ_09_underscore = az_AZ_underscore + digit; 
az_AZ_hyphen = hyphen + letter;
az_AZ_09_hiphen = az_AZ_hyphen + digit;
LPAR = Suppress('('); RPAR = Suppress(')')

# identifiers
identifier = Combine(Word(az_AZ_underscore) + Optional(Word(az_AZ_09_underscore)))
# identifiers
identifier_reserved = Combine(Word(az_AZ_hyphen) + Optional(Word(az_AZ_09_hiphen)))

# numbers
hexadecimal = Word(hexnums, min=1)
integer = Word(digit, min=1)
decimal = Combine('.' + integer | integer + Optional('.' + integer))


# value values
color_hex = Combine('#' +  hexadecimal ) 
at_identifier =  Combine('@' + identifier)
arg = at_identifier | color_hex | decimal | quotedString 




function_call = identifier + LPAR + Optional(delimitedList(arg)) + RPAR

value =  Group(color_hex | at_identifier | function_call)

print(value.parseString('a(b())'))

我想做的事情是像这样 arg = at_identifier | color_hex | decimal | quotedString | function_call,但这不可能,因为变量function_call还没有声明。

我该如何使用Pyparsing来解析嵌套的函数调用呢?

1 个回答

3

你真的很接近了。要定义这样的递归语法,你需要提前声明一下嵌套的表达式。

正如你已经看到的,这段代码:

arg = at_identifier | color_hex | decimal | quotedString
function_call = identifier + LPAR + Optional(delimitedList(arg)) + RPAR

只能解析那些参数不是函数调用的函数调用。

为了递归定义这个,我们首先需要定义一个空的占位符表达式,使用 Forward():

function_call = Forward()

我们现在还不知道这个表达式里会放什么,但我们知道它会是一个有效的参数类型。既然现在已经声明了,我们就可以使用它了:

arg = at_identifier | color_hex | decimal | quotedString | function_call

现在我们已经定义了参数,我们可以定义函数调用里会放什么。这里我们不能用普通的 Python 赋值符号 '=',而是要用一个可以修改函数调用的操作符,而不是重新定义它。Pyparsing 允许你使用 <<= 或 << 操作符(推荐使用第一个):

function_call <<= identifier + LPAR + Optional(delimitedList(arg)) + RPAR

现在这已经足够解析你给出的示例字符串了,但你失去了对实际嵌套结构的可见性。如果你像这样对函数调用进行分组:

function_call <<= Group(identifier + LPAR + Group(Optional(delimitedList(arg))) + RPAR)

你将总是能从函数调用中得到一个可预测的结构(一个名称和解析后的参数),而函数调用本身也会被分组。

撰写回答