You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Ply Lex Yacc中让 仅在指定规则中作为Token,其余情况忽略?

我之前用Ply处理过类似的换行规则需求,刚好能给你一套可行的实现方案。核心思路是利用Ply的**lexer状态(states)**来区分两种场景:需要换行作为语法结束符的def定义场景,以及其他忽略换行只统计行数的场景。

步骤1:定义Lexer状态

首先在解析器类里声明一个专属的def_mode状态,用来隔离def定义的语境:

from ply import lex, yacc

class DefinitionParser:
    def __init__(self):
        self.lexer = lex.lex(module=self)
        self.parser = yacc.yacc(module=self)
    
    # 定义排他性状态:进入该状态后仅处理该状态下的Token规则
    states = (
        ('def_mode', 'exclusive'),
    )

步骤2:处理普通模式下的Token

在默认的INITIAL状态(普通场景)下,换行仅统计行数但不返回Token;遇到def关键字时切换到def模式:

# 普通模式:识别def关键字,切换到def模式并返回DEF Token
def t_INITIAL_def(self, t):
    r'def'
    t.lexer.begin('def_mode')
    t.type = 'DEF'
    return t

# 普通模式:换行仅更新行号,不返回Token(相当于忽略)
def t_INITIAL_NEWLINE(self, t):
    r'\n+'
    t.lexer.lineno += len(t.value)

# 普通模式:忽略空格和制表符
def t_ignore_WHITESPACE(self, t):
    r'[ \t]+'
    pass

# 普通模式:处理其他通用标识符(按需调整)
def t_INITIAL_ID(self, t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    t.type = 'ID'
    return t

步骤3:处理def模式下的Token

def_mode状态下,我们需要确保定义必须以换行结尾,同时拦截非法输入:

# def模式:处理定义的名称标识符
def t_def_mode_ID(self, t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    t.type = 'ID'
    return t

# def模式:换行作为定义结束符,返回Token并切回普通模式
def t_def_mode_NEWLINE(self, t):
    r'\n+'
    t.lexer.lineno += len(t.value)
    t.lexer.begin('INITIAL')
    t.type = 'NEWLINE'
    return t

# def模式:如果未换行就遇到新的def,直接抛出语法错误
def t_def_mode_def(self, t):
    r'def'
    print(f"语法错误:定义必须以换行结尾,行号 {t.lexer.lineno}")
    t.lexer.skip(1)

# def模式:忽略空格
def t_def_mode_ignore_WHITESPACE(self, t):
    r'[ \t]+'
    pass

# 通用错误处理
def t_error(self, t):
    print(f"非法字符 '{t.value[0]}',行号 {t.lexer.lineno}")
    t.lexer.skip(1)

步骤4:编写语法规则

最后在语法规则里明确def定义的结构,必须以NEWLINE结尾:

# 顶层规则:支持多个定义或空输入
def p_top_level(p):
    '''top_level : definition top_level
                 | empty'''
    pass

def p_empty(p):
    'empty :'
    pass

# 定义规则:严格要求 DEF + 名称 + NEWLINE
def p_definition(p):
    'definition : DEF ID NEWLINE'
    print(f"成功解析定义:{p[2]}")

测试验证

现在测试你的示例场景:

  • 合法输入:
    def1
    def2
    def3
    
    会输出三个成功解析的定义。
  • 非法输入:
    def1 def2 def3
    
    会在遇到第二个def时抛出“定义必须以换行结尾”的错误,完全符合你的需求。

这种方式的好处是逻辑清晰,用状态隔离了两种换行处理场景,而且扩展性强——如果以后需要其他需要换行的语法,只需要新增对应的状态即可。

内容的提问来源于stack exchange,提问作者ibi0tux

火山引擎 最新活动