You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Lark递归读取多个include引用的配置文件?

解决Lark解析配置文件时的目录Include合并问题

问题背景

现有自定义格式的配置文件,其中include语句指向目录时,需要读取该目录下所有.conf文件,并将解析后的内容合并到原语法树中。已完成Lark语法规则编写,且能通过Transformer获取到include指定的目录路径,但不清楚如何合并解析后的子文件内容到原树。

解决方案

核心思路是在Transformer的include处理方法中,遍历目标目录下的所有.conf文件,逐一解析后提取其语法树的根节点子元素,替换原include节点:

  • 导入os模块处理目录遍历与路径拼接
  • 在Transformer中复用已创建的Lark解析器实例,避免重复初始化开销
  • 遍历目标目录下所有.conf文件,按文件名排序保证解析顺序稳定
  • 提取每个子文件语法树中start节点的所有子元素,合并后返回替换原include节点

修改后的完整代码

Transformer与解析逻辑

#!/usr/bin/env python3
from lark import Lark, Transformer, Tree
from pprint import pprint
import os

class ExpandIncludes(Transformer):
    def __init__(self, parser, base_dir):
        self.parser = parser
        self.base_dir = base_dir  # 主配置文件所在目录,用于处理相对路径

    def include(self, item):
        path = item[0]
        dir_path = strip_quotes(path.children[0].value)
        # 处理相对路径:基于主配置文件所在目录拼接
        full_dir = os.path.join(self.base_dir, dir_path)
        
        included_nodes = []
        # 遍历目录下所有.conf文件,按文件名排序保证顺序稳定
        for filename in sorted(os.listdir(full_dir)):
            if filename.endswith('.conf'):
                file_path = os.path.join(full_dir, filename)
                with open(file_path, 'r') as f:
                    content = f.read()
                    # 解析子文件语法树
                    sub_tree = self.parser.parse(content)
                    # 提取start节点下的所有子元素(value或COMMENT)
                    included_nodes.extend(sub_tree.children)
        
        # 返回合并后的节点列表,替换原include节点
        return included_nodes

def strip_quotes(s):
    if s.startswith('"') and s.endswith('"'):
        return s[1:-1]

def parse_file(conf_grammar, conf_file):
    # 创建解析器实例,复用给Transformer避免重复初始化
    conf_parser = Lark(conf_grammar, parser='earley')
    # 获取主配置文件所在目录,用于处理相对路径
    base_dir = os.path.dirname(os.path.abspath(conf_file))
    
    with open(conf_file, 'r') as f:
        test_conf = f.read()
        tree = conf_parser.parse(test_conf)
        # 初始化Transformer时传入解析器和基础目录
        tree = ExpandIncludes(conf_parser, base_dir).transform(tree)
    return tree

if __name__ == '__main__':
    with open('parseconf/try.lark') as f:
        conf_grammar = f.read()
        tree = parse_file(conf_grammar, 'parseconf/test.conf')
        print(tree.pretty())

关键修改说明

  • Transformer初始化:新增parser和base_dir参数,复用解析器降低开销,同时解决相对路径解析问题
  • 目录遍历:过滤.conf文件并按文件名排序,保证解析顺序可预测
  • 节点合并:将子文件语法树的start子节点全部提取合并,原include节点会被这些节点替代
  • 路径处理:通过os.path.join拼接主配置目录与include路径,确保相对路径正确解析

验证效果

运行修改后的代码,原配置中的include "parseconf/dir_with_many_files"节点会被替换为该目录下所有.conf文件解析后的语法树节点,最终输出的语法树会包含所有合并后的内容。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:14:54