MatIEC 编译器 Stage1_2 模块深度解析
深入探索 IEC 61131-3 标准编译器的核心实现
前言
欢迎来到 MatIEC 编译器的世界!如果你对工业自动化编程感兴趣,或者想深入了解编译器是如何工作的,那么你来对地方了。本文将带你深入探索 MatIEC 编译器的 Stage1_2 模块——这是整个编译器的\”大脑\”,负责将人类可读的代码转换成计算机能理解的抽象语法树。
项目背景:什么是 MatIEC?
MatIEC 是一个开源的 IEC 61131-3 标准编程语言编译器。可能你会问:\”IEC 61131-3 又是什么?\”简单来说,这是国际电工委员会制定的可编程控制器编程语言标准,广泛应用于工业自动化领域,比如工厂生产线、机器人控制等。
MatIEC 的强大之处在于它支持该标准定义的多种编程语言:
- ST (Structured Text) – 结构化文本,类似 Pascal 的高级语言
- IL (Instruction List) – 指令表,类似汇编的低级语言
- SFC (Sequential Function Chart) – 顺序功能图,图形化的流程控制语言
想象一下,你可以在同一个项目中自由切换使用这些语言,而 MatIEC 能够完美地处理它们!
MatIEC 的应用生态
MatIEC 不仅仅是一个独立的编译器,它还是多个知名开源项目的核心组件,为工业自动化领域提供了强大的技术支持。让我们来看看哪些项目在使用 MatIEC:
1. Beremiz – 全能的 PLC 开发平台
Beremiz 是一款功能强大的开源 PLC(可编程逻辑控制器)开发软件,专为工业自动化控制系统的编程和调试而设计。它提供了一个直观的图形化编程界面,让工程师能够轻松创建、测试和部署自动化程序。
核心特性:
- ✅ 完全符合 IEC 61131-3 标准
- ✅ 支持五种编程语言(LD、ST、IL、FBD、SFC)
- ✅ 内置 HMI(人机界面)设计能力
- ✅ 强大的兼容性和灵活性
- ✅ 支持多种硬件平台
Beremiz 将 MatIEC 作为其编译器后端,负责将工程师编写的 IEC 61131-3 代码编译成可执行的 PLC 程序。这使得 Beremiz 能够支持完整的 IEC 61131-3 语言特性,为工程师提供专业级的开发体验。
2. OpenPLC – 研究与教育的利器
OpenPLC 是一个开源的可编程逻辑控制器平台,旨在促进工业控制系统(ICS)的研究和教育。它支持 Windows、Linux 和 macOS 等多种操作系统,让研究人员和学生能够在不同的平台上学习和实验 PLC 编程。
核心特性:
- ✅ 完全兼容 IEC 61131-3 标准
- ✅ 提供梯形图(LD)、结构化文本(ST)、功能块图(FBD)等多种编程语言
- ✅ 支持多种 SCADA 协议,如 Modbus/TCP 和 DNP3
- ✅ 集成开源 HMI 解决方案
- ✅ 跨平台支持
OpenPLC 使用 MatIEC 作为其编译引擎,将用户编写的控制逻辑编译成可在不同硬件上运行的可执行代码。这使得 OpenPLC 成为工业控制系统安全研究、教育实验和原型开发的理想平台。
3. 更多应用场景
除了 Beremiz 和 OpenPLC,MatIEC 还被广泛应用于:
- 工业自动化解决方案:各种基于 IEC 61131-3 标准的自动化控制系统
- 教育培训:高校和研究机构用于 PLC 编程教学
- 原型开发:快速开发和测试自动化控制算法
- 嵌入式系统:在嵌入式平台上实现 PLC 功能
这些项目共同构成了一个繁荣的开源工业自动化生态系统,而 MatIEC 正是这个生态系统的技术基石之一。
编译器全景:从代码到可执行文件
在深入 Stage1_2 之前,让我们先鸟瞰整个编译器的架构。MatIEC 采用经典的四阶段流水线设计,每个阶段各司其职,协同工作。
#mermaid-svg-bbR8P4wDPCK1UR4K{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bbR8P4wDPCK1UR4K .error-icon{fill:#552222;}#mermaid-svg-bbR8P4wDPCK1UR4K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bbR8P4wDPCK1UR4K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bbR8P4wDPCK1UR4K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bbR8P4wDPCK1UR4K .marker.cross{stroke:#333333;}#mermaid-svg-bbR8P4wDPCK1UR4K svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bbR8P4wDPCK1UR4K p{margin:0;}#mermaid-svg-bbR8P4wDPCK1UR4K .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster-label text{fill:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster-label span{color:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster-label span p{background-color:transparent;}#mermaid-svg-bbR8P4wDPCK1UR4K .label text,#mermaid-svg-bbR8P4wDPCK1UR4K span{fill:#333;color:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K .node rect,#mermaid-svg-bbR8P4wDPCK1UR4K .node circle,#mermaid-svg-bbR8P4wDPCK1UR4K .node ellipse,#mermaid-svg-bbR8P4wDPCK1UR4K .node polygon,#mermaid-svg-bbR8P4wDPCK1UR4K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bbR8P4wDPCK1UR4K .rough-node .label text,#mermaid-svg-bbR8P4wDPCK1UR4K .node .label text,#mermaid-svg-bbR8P4wDPCK1UR4K .image-shape .label,#mermaid-svg-bbR8P4wDPCK1UR4K .icon-shape .label{text-anchor:middle;}#mermaid-svg-bbR8P4wDPCK1UR4K .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bbR8P4wDPCK1UR4K .rough-node .label,#mermaid-svg-bbR8P4wDPCK1UR4K .node .label,#mermaid-svg-bbR8P4wDPCK1UR4K .image-shape .label,#mermaid-svg-bbR8P4wDPCK1UR4K .icon-shape .label{text-align:center;}#mermaid-svg-bbR8P4wDPCK1UR4K .node.clickable{cursor:pointer;}#mermaid-svg-bbR8P4wDPCK1UR4K .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bbR8P4wDPCK1UR4K .arrowheadPath{fill:#333333;}#mermaid-svg-bbR8P4wDPCK1UR4K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bbR8P4wDPCK1UR4K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bbR8P4wDPCK1UR4K .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bbR8P4wDPCK1UR4K .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bbR8P4wDPCK1UR4K .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bbR8P4wDPCK1UR4K .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster text{fill:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K .cluster span{color:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bbR8P4wDPCK1UR4K .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bbR8P4wDPCK1UR4K rect.text{fill:none;stroke-width:0;}#mermaid-svg-bbR8P4wDPCK1UR4K .icon-shape,#mermaid-svg-bbR8P4wDPCK1UR4K .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bbR8P4wDPCK1UR4K .icon-shape p,#mermaid-svg-bbR8P4wDPCK1UR4K .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bbR8P4wDPCK1UR4K .icon-shape rect,#mermaid-svg-bbR8P4wDPCK1UR4K .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bbR8P4wDPCK1UR4K .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bbR8P4wDPCK1UR4K .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bbR8P4wDPCK1UR4K :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
源代码文件 .st/.il
Stage 1_2: 词法与语法分析
Stage 3: 语义分析
Stage 4: 代码生成
Stage 4+1: 二进制代码生成
各阶段职责一览
| Stage 1 | Flex | 词法分析:将文本拆解成 Token | Token 流 |
| Stage 2 | Bison | 语法分析:构建语法树 | 抽象语法树 (AST) |
| Stage 3 | 自研 | 语义分析:类型检查、常量折叠 | 优化后的 AST |
| Stage 4 | C++ | 代码生成:生成目标语言代码 | C/Java 等代码 |
| Stage 4+1 | GCC/其他 | 二进制生成:编译成可执行文件 | 可执行程序 |
你可以把这想象成一个工厂流水线:原材料(源代码)经过一道道工序的加工,最终变成精美的产品(可执行程序)。Stage1_2 就是这条流水线的第一站,也是至关重要的一站。
Stage1_2:编译器的\”翻译官\”
Stage1_2 是编译器的入口模块,它的核心任务是将 IEC 61131-3 源代码转换成抽象语法树(AST)。虽然它叫 Stage1_2,但实际上它巧妙地将 Stage 1(词法分析)和 Stage 2(语法分析)融合在一起,形成一个紧密协作的整体。
模块内部结构
让我们打开 Stage1_2 的\”黑盒子\”,看看里面都有什么:
#mermaid-svg-SdI3rP3JsfeYbUJS{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SdI3rP3JsfeYbUJS .error-icon{fill:#552222;}#mermaid-svg-SdI3rP3JsfeYbUJS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SdI3rP3JsfeYbUJS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SdI3rP3JsfeYbUJS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SdI3rP3JsfeYbUJS .marker.cross{stroke:#333333;}#mermaid-svg-SdI3rP3JsfeYbUJS svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SdI3rP3JsfeYbUJS p{margin:0;}#mermaid-svg-SdI3rP3JsfeYbUJS .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster-label text{fill:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster-label span{color:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster-label span p{background-color:transparent;}#mermaid-svg-SdI3rP3JsfeYbUJS .label text,#mermaid-svg-SdI3rP3JsfeYbUJS span{fill:#333;color:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS .node rect,#mermaid-svg-SdI3rP3JsfeYbUJS .node circle,#mermaid-svg-SdI3rP3JsfeYbUJS .node ellipse,#mermaid-svg-SdI3rP3JsfeYbUJS .node polygon,#mermaid-svg-SdI3rP3JsfeYbUJS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SdI3rP3JsfeYbUJS .rough-node .label text,#mermaid-svg-SdI3rP3JsfeYbUJS .node .label text,#mermaid-svg-SdI3rP3JsfeYbUJS .image-shape .label,#mermaid-svg-SdI3rP3JsfeYbUJS .icon-shape .label{text-anchor:middle;}#mermaid-svg-SdI3rP3JsfeYbUJS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SdI3rP3JsfeYbUJS .rough-node .label,#mermaid-svg-SdI3rP3JsfeYbUJS .node .label,#mermaid-svg-SdI3rP3JsfeYbUJS .image-shape .label,#mermaid-svg-SdI3rP3JsfeYbUJS .icon-shape .label{text-align:center;}#mermaid-svg-SdI3rP3JsfeYbUJS .node.clickable{cursor:pointer;}#mermaid-svg-SdI3rP3JsfeYbUJS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SdI3rP3JsfeYbUJS .arrowheadPath{fill:#333333;}#mermaid-svg-SdI3rP3JsfeYbUJS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SdI3rP3JsfeYbUJS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SdI3rP3JsfeYbUJS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SdI3rP3JsfeYbUJS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SdI3rP3JsfeYbUJS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SdI3rP3JsfeYbUJS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster text{fill:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS .cluster span{color:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SdI3rP3JsfeYbUJS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SdI3rP3JsfeYbUJS rect.text{fill:none;stroke-width:0;}#mermaid-svg-SdI3rP3JsfeYbUJS .icon-shape,#mermaid-svg-SdI3rP3JsfeYbUJS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SdI3rP3JsfeYbUJS .icon-shape p,#mermaid-svg-SdI3rP3JsfeYbUJS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SdI3rP3JsfeYbUJS .icon-shape rect,#mermaid-svg-SdI3rP3JsfeYbUJS .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SdI3rP3JsfeYbUJS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SdI3rP3JsfeYbUJS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SdI3rP3JsfeYbUJS :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Stage1_2 模块大家庭
stage1_2.cc总指挥官
iec_flex.ll词法分析专家
iec_bison.yy语法分析专家
create_enumtype_conversion_functions.cc枚举类型助手
stage1_2.hh对外接口
stage1_2_priv.hh内部通信
每个文件都有自己独特的职责:
- stage1_2.cc:总指挥官,协调各个组件的工作
- stage1_2.hh:对外接口,定义了模块的公共 API
- stage1_2_priv.hh:内部通信协议,定义了组件间的私有接口
- iec_flex.ll:词法分析专家,负责识别文本中的单词和符号
- iec_bison.yy:语法分析专家,负责理解代码的语法结构
- create_enumtype_conversion_functions.cc:枚举类型助手,处理枚举类型转换
核心文件深度解析
1. stage1_2.cc – 总指挥官
这是整个 Stage1_2 模块的核心文件,你可以把它想象成一位经验丰富的指挥官,统筹全局。
简洁而强大的入口:
int stage1_2(const char *filename, symbol_c **tree_root) {
// 只调用 stage2 (bison),因为 stage2 会自动调用 stage1 (flex)
return stage2__(filename, tree_root);
}
看起来很简单对吧?但背后隐藏着精妙的设计。Stage1_2 并不直接调用词法分析器,而是让语法分析器(Bison)在需要时自动调用词法分析器(Flex)。这种设计让两个分析器之间的协作更加自然流畅。
全局状态管理:
指挥官需要时刻掌握全局状态,stage1_2.cc 维护了多个全局变量来控制解析过程:
| preparse_state__ | 标记是否处于预解析状态 | 双遍解析时区分第一遍和第二遍 |
| goto_body_state__ | 指示进入代码体状态 | 解析完变量声明后 |
| goto_sfc_qualifier_state__ | 指示进入 SFC 限定符状态 | 解析 SFC 动作限定符时 |
| goto_sfc_priority_state__ | 指示进入 SFC 优先级状态 | 解析 SFC 任务优先级时 |
| goto_task_init_state__ | 指示进入任务初始化状态 | 解析配置中的任务时 |
| pop_state__ | 指示弹出状态栈 | 离开某个语法结构时 |
这些状态变量就像指挥官手中的旗帜,通过它们可以精确控制词法分析器的行为。
符号表管理:
指挥官还需要管理\”字典\”——符号表。Stage1_2 维护了三个重要的符号表:
// 库元素符号表 – 存储所有函数、FB、类型、程序、配置的名称
library_element_symtable_t library_element_symtable;
// 变量名符号表 – 存储当前正在解析的函数中声明的变量
variable_name_symtable_t variable_name_symtable;
// 直接变量符号表 – 存储直接变量(如 %IX0.0)
direct_variable_symtable_t direct_variable_symtable;
这些符号表在整个解析过程中扮演着关键角色,它们帮助编译器识别哪些是已知的名称,哪些是新声明的标识符。
2. iec_flex.ll – 词法分析专家
Flex 词法分析器是 Stage1_2 的\”眼睛\”,它负责将源代码文本转换成 Token 流。就像我们阅读文章时,会自动识别出单词、标点符号一样,Flex 也能识别出代码中的各种元素。
精心配置的 Flex 选项:
%option never-interactive // 非交互模式,提高效率
%option pointer // 使用指针存储 token 文本
%option case-insensitive // 忽略大小写(IEC 61131-3 标准要求)
%option yylineno // 自动跟踪行号,方便错误定位
%option stack // 支持状态栈,处理嵌套结构
%option noyy_top_state // 不生成不需要的函数,减少代码体积
这些配置选项体现了 Flex 的灵活性和强大功能。特别是 case-insensitive 选项,它让 Flex 能够正确处理 IEC 61131-3 标准中不区分大小写的要求。
复杂而优雅的状态机:
Flex 最精彩的部分在于它的状态机设计。IEC 61131-3 支持多种编程语言,而且它们可以在同一个文件中混合使用。为了正确处理这种情况,Flex 使用了一个复杂的状态机来跟踪当前的解析上下文。
#mermaid-svg-mFYwOq0wcDYBCfqJ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mFYwOq0wcDYBCfqJ .error-icon{fill:#552222;}#mermaid-svg-mFYwOq0wcDYBCfqJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mFYwOq0wcDYBCfqJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .marker.cross{stroke:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mFYwOq0wcDYBCfqJ p{margin:0;}#mermaid-svg-mFYwOq0wcDYBCfqJ defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-mFYwOq0wcDYBCfqJ g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-mFYwOq0wcDYBCfqJ g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-mFYwOq0wcDYBCfqJ g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-mFYwOq0wcDYBCfqJ g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-mFYwOq0wcDYBCfqJ .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-mFYwOq0wcDYBCfqJ .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mFYwOq0wcDYBCfqJ .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mFYwOq0wcDYBCfqJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mFYwOq0wcDYBCfqJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mFYwOq0wcDYBCfqJ .edgeLabel .label text{fill:#333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .label div .edgeLabel{color:#333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-mFYwOq0wcDYBCfqJ .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-mFYwOq0wcDYBCfqJ .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-mFYwOq0wcDYBCfqJ .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ #statediagram-barbEnd{fill:#333333;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .cluster-label,#mermaid-svg-mFYwOq0wcDYBCfqJ .nodeLabel{color:#131300;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .note-edge{stroke-dasharray:5;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-note text{fill:black;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram-note .nodeLabel{color:black;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagram .edgeLabel{color:red;}#mermaid-svg-mFYwOq0wcDYBCfqJ #dependencyStart,#mermaid-svg-mFYwOq0wcDYBCfqJ #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-mFYwOq0wcDYBCfqJ .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mFYwOq0wcDYBCfqJ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始解析
预解析时遇到 POU
遇到 CONFIGURATION
遇到 FUNCTION/FB/PROGRAM
遇到第一个 VAR
遇到 VAR
遇到 END_VAR
遇到最后一个 END_VAR
识别为 ST 代码
识别为 IL 代码
识别为 SFC 代码
遇到 END_FUNCTION 等
遇到 END_FUNCTION 等
遇到 END_FUNCTION 等
解析 Action 时
期待限定符
遇到 END_FUNCTION 等
遇到 END_CONFIGURATION
INITIAL

