1. A2L文件结构完整性检查

任务目标

检查A2L文件的基本结构完整性,验证必需部分(HEADER、MODULE、COMPU_METHOD等)是否存在且顺序正确。本案例以最小可复现的A2L片段为输入,通过Python脚本解析并输出结构检查结果。

实现方案

使用Python标准库编写一个轻量级结构检查器,不依赖第三方A2L解析库。核心逻辑:逐行扫描A2L文件,识别顶层关键字(以begin起始的块定义),维护一个期望顺序列表,检查必需块是否出现、是否重复、顺序是否错乱。

关键完整代码

import sys
import re

# A2L必需顶层块及其合法出现顺序(按ASAP2标准)
REQUIRED_BLOCKS = [
    ("ASAP2_VERSION", False),   # (块名, 是否允许缺失)
    ("HEADER", False),
    ("MODULE", False),
    ("COMPU_METHOD", True),     # 允许缺失(部分A2L可能无全局COMPU_METHOD)
]

# 实际解析时需跟踪MODULE内部,但本案例仅检查顶层结构
def check_a2l_structure(filepath):
    errors = []
    found_blocks = []  # 按出现顺序记录顶层块名
    
    with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
        lines = f.readlines()
    
    # 预处理:去除注释(/ * ... * / 和 //)
    code_lines = []
    in_block_comment = False
    for line in lines:
        line = line.strip()
        if in_block_comment:
            end_idx = line.find('*/')
            if end_idx != -1:
                line = line[end_idx+2:].strip()
                in_block_comment = False
            else:
                continue
        # 去掉行内块注释开始
        while '/*' in line:
            start_idx = line.find('/*')
            end_idx = line.find('*/', start_idx)
            if end_idx == -1:
                line = line[:start_idx].strip()
                in_block_comment = True
                break
            else:
                line = (line[:start_idx] + ' ' + line[end_idx+2:]).strip()
        # 去掉//注释
        line = re.sub(r'//.*$', '', line).strip()
        if line:
            code_lines.append(line)
    
    # 识别顶层块:行首为 "begin 块名" 且不在其他块内部
    # 简化处理:通过缩进判断层级(A2L标准使用缩进表示嵌套)
    # 顶层块缩进为0(行首无空格)
    for line in code_lines:
        if line.startswith('begin '):
            indent = len(line) - len(line.lstrip())
            if indent == 0:
                block_name = line.split()[1].strip()
                found_blocks.append(block_name)
    
    # 检查必需块是否存在
    required_names = [name for name, optional in REQUIRED_BLOCKS if not optional]
    for name in required_names:
        if name not in found_blocks:
            errors.append(f"缺少必需块: {name}")
    
    # 检查顺序(按REQUIRED_BLOCKS定义的顺序)
    expected_order = [name for name, _ in REQUIRED_BLOCKS]
    # 过滤掉文件中未出现的块,只比较实际出现的块的相对顺序
    actual_order = [b for b in found_blocks if b in expected_order]
    # 去重后检查顺序
    seen = []
    for b in actual_order:
        if b not in seen:
            seen.append(b)
    
    # 检查seen是否满足expected_order的子序列关系
    idx = 0
    for b in seen:
        while idx < len(expected_order) and expected_order[idx] != b:
            idx += 1
        if idx >= len(expected_order):
            errors.append(f"块顺序错误: {b} 出现在不应出现的位置")
            break
        idx += 1
    
    # 检查重复(同一块出现多次,MODULE除外——但标准中MODULE只能有一个)
    from collections import Counter
    counter = Counter(found_blocks)
    for name, count in counter.items():
        if count > 1 and name in ('HEADER', 'MODULE', 'ASAP2_VERSION'):
            errors.append(f"块 {name} 重复出现 {count} 次")
    
    return found_blocks, errors

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法: python a2l_check.py <文件路径>")
        sys.exit(1)
    
    blocks, errs = check_a2l_structure(sys.argv[1])
    print("检测到的顶层块(按出现顺序):")
    for b in blocks:
        print(f"  - {b}")
    
    if errs:
        print("\n结构错误:")
        for e in errs:
            print(f"  [ERROR] {e}")
        sys.exit(1)
    else:
        print("\n结构检查通过: 所有必需块存在且顺序正确")
        sys.exit(0)

运行与验证

环境要求:Python 3.6+,无第三方依赖。

测试输入文件(保存为 sample.a2l):

ASAP2_VERSION 1 51
begin HEADER
  VERSION "1.0"
end HEADER

begin MODULE
  NAME "DemoModule"
  begin COMPU_METHOD
    NAME "CM_uint8"
    CONVERSION 1 0 0 255
  end COMPU_METHOD
end MODULE

运行命令:

python a2l_check.py sample.a2l

预期输出:

检测到的顶层块(按出现顺序):
  - ASAP2_VERSION
  - HEADER
  - MODULE
结构检查通过: 所有必需块存在且顺序正确

验证方法:

  1. 将上述sample.a2l保存后运行脚本,应看到“结构检查通过”且退出码为0。
  2. 修改测试文件(例如删除HEADER块或将MODULE移到HEADER之前),重新运行,脚本应报告对应的缺失或顺序错误,退出码为1。

判定标准:脚本能正确识别缺失块、顺序错乱和重复块三类结构问题,且对合法文件不产生误报。