# 通用机器人编程语法规范

版本：0.1  
适用范围：离线编程、虚拟控制器、虚拟调试、多品牌后处理  
目标品牌：ABB RAPID、FANUC TP/LS/KAREL 语义、KUKA KRL

## 1. 设计目标

本规范定义一种用于离线编程和虚拟调试的通用机器人程序语言，暂定名为 GRL，Generic Robot Language。GRL 不是复制某一家机器人厂商语言，而是抽象 ABB、FANUC、KUKA 等主流工业机器人共同具备的程序语义，形成稳定、可解析、可仿真、可自动生成、可后处理的中性机器人程序。

核心目标：

1. 支持离线编程的完整流程：目标点、路径、工艺、IO、等待、流程控制、报警、仿真执行、报告和后处理。
2. 方便由规划点、CAD 曲线、工艺模板、AI 规划器自动生成程序。
3. 方便转换为 ABB RAPID、FANUC LS/TP 风格文本、KUKA KRL。
4. 方便反向导入品牌程序，恢复为统一 IR、GRL 和 OLP 对象模型。
5. 程序文本稳定、可读、可 diff，便于版本管理。
6. 语义确定，编译后能生成统一可执行 IR，虚拟控制器不依赖品牌控制器细节。

非目标：

1. 不追求完全复刻某一品牌控制器所有系统变量和内部调度细节。
2. 不直接作为真实机器人安全运行的唯一依据，后处理输出必须经过品牌控制器验证和现场低速试运行。
3. 不把 CAD、碰撞检测、机器人运动学全部写入语言本身；这些由 OLP 模型和仿真内核承担。

## 2. 对标原则

### 2.1 品牌能力映射

| 能力 | ABB RAPID | FANUC TP/LS | KUKA KRL | GRL |
| --- | --- | --- | --- | --- |
| 程序模块 | `MODULE` | TP 程序、KAREL 程序 | `.src/.dat`、`DEF` | `module` |
| 子程序 | `PROC` | `CALL` | `DEF` 子程序 | `proc` |
| 函数 | `FUNC` | KAREL function | `DEFFCT` | `func` |
| 中断/陷阱 | `TRAP`、interrupt | 条件监控、后台逻辑 | `INTERRUPT` | `trap`、`interrupt` |
| 关节运动 | `MoveJ` | `J P[...]` | `PTP` | `movej` |
| 直线运动 | `MoveL` | `L P[...]` | `LIN` | `movel` |
| 圆弧运动 | `MoveC` | `C P[...]` | `CIRC` | `movec` |
| 笛卡尔目标点 | `robtarget` | `P[]`、`PR[]` | `E6POS` | `pose_target` |
| 关节目标点 | `jointtarget` | joint position data | `E6AXIS` | `joint_target` |
| 工具 | `tooldata` | UTOOL | `$TOOL` | `tool` |
| 工件/基坐标 | `wobjdata` | UFRAME | `$BASE` | `frame` |
| 速度 | `speeddata` | `%`、`mm/sec` | `$VEL`、`$ACC` | `speed` |
| 过渡 | `zonedata`、`fine` | `FINE`、`CNT` | `C_DIS`、`C_PTP`、`APO` | `zone` |
| IO | `SetDO`、`WaitDI` | `DO[]`、`DI[]`、`WAIT` | `$OUT[]`、`$IN[]`、`WAIT FOR` | `io.*`、`wait` |
| 标签跳转 | `GOTO` | `LBL`、`JMP` | `GOTO` | `label`、`jump` |

### 2.2 中性语法风格

GRL 采用“声明式数据 + 结构化程序 + Path/Operation 对象”的风格：

1. 目标点、工具、坐标系、速度、过渡作为数据声明。
2. 程序流程使用 `proc`、`if`、`while`、`for`、`switch` 等结构化控制。
3. 短程序和调试程序可直接写 `movej/movel/movec`。
4. 自动离线编程优先生成 `path` 和 `operation`，程序中通过 `run_path`、`run_operation` 调用。
5. 所有运动语句显式绑定目标点、速度、过渡、工具、坐标系，允许默认值，但编译到 IR 前必须解析为确定值。
6. 品牌特性通过 `post_hint` 和 `@brand.*` 元数据保留，不污染通用语义。

## 3. 文件与工程结构

推荐一个 OLP 项目使用以下结构：

```text
project.json
robots/
  robot_1.urdf
  robot_1.meta.json
programs/
  main.grl
  weld.grl
targets/
  main.targets.json
paths/
  weld_path.path.json
operations/
  weld_op.operation.json
io/
  io_map.json
post/
  abb.profile.json
  fanuc.profile.json
  kuka.profile.json
generated/
  abb/
  fanuc/
  kuka/
reports/
  reachability.json
  cycle_time.json
```

GRL 文件扩展名建议使用 `.grl`。

## 4. 词法规则

### 4.1 编码和大小写

1. 文件编码使用 UTF-8。
2. 关键字建议小写。
3. 标识符大小写敏感，推荐使用 `snake_case`。
4. 后处理器输出到品牌程序时按品牌习惯调整大小写。

### 4.2 注释

```text
// 单行注释

/*
  多行注释
*/
```

自动生成程序应在关键对象上保留来源注释，但不要为每个采样点生成大量冗余注释。

### 4.3 标识符

```text
identifier = letter { letter | digit | "_" }
```

示例：

```text
pick_path
weld_op_01
T_curve_032_000
```

不建议使用中文标识符。UI 可显示中文别名，但程序内部名称应保持 ASCII，便于后处理和版本管理。

### 4.4 字面量和单位

支持带单位数值：

```text
100 mm
0.25 m
180 deg
3.14159 rad
300 mm/s
50 %
2 s
200 ms
2.5 kg
```

编译器内部统一规范化：

| 物理量 | 内部单位 |
| --- | --- |
| 长度 | meter |
| 角度 | radian |
| 时间 | second |
| 质量 | kilogram |
| 线速度 | meter/second |
| 角速度 | radian/second |
| 加速度 | meter/second^2 或 radian/second^2 |

### 4.5 保留关键字

```text
language module import end
persistent const var
robot tool frame load target speed zone path operation process
proc func return call
if elseif else switch case default while for to step break continue
label jump
movej movel movec run_path run_operation
set_tool set_frame set_speed set_zone
wait pulse timer
io true false all any rising falling changed
trap interrupt enable disable raise alarm try catch finally
task sync
post_hint source defaults point event before after at
joint_target pose_target pose poseq joints robot_config ext_axis
fine continuous cnt z
```

## 5. 顶层结构

### 5.1 最小文件

```text
language grl 0.1

module Main
  proc main()
    // program entry
  end
end
```

`language grl 0.1` 用于版本控制。后续语言升级时，编译器可按版本选择兼容规则。

### 5.2 顶层声明

```text
module ModuleName
  import CommonTools

  persistent tool gripper = ...
  persistent frame fixture = ...
  const speed v_pick = linear(300 mm/s)
  const zone z_pick = z10

  target home = ...
  path pick_path { ... }
  operation pick_op { ... }

  proc main()
    ...
  end
end
```

顶层可包含：

1. `import`
2. `persistent`
3. `const`
4. `target`
5. `path`
6. `operation`
7. `proc`
8. `func`
9. `trap`
10. `task`
11. `post_hint`

## 6. 类型系统

### 6.1 基础类型

| 类型 | 说明 |
| --- | --- |
| `bool` | 布尔值 |
| `int` | 整数 |
| `real` | 浮点数 |
| `string` | 字符串 |
| `time` | 时间 |
| `length` | 长度 |
| `angle` | 角度 |
| `percent` | 百分比 |

### 6.2 机器人类型

| 类型 | 说明 |
| --- | --- |
| `pose` | 笛卡尔位姿 |
| `joint_array` | 关节数组 |
| `pose_target` | 笛卡尔目标点 |
| `joint_target` | 关节目标点 |
| `tool` | 工具 TCP、负载、惯量 |
| `frame` | 工件坐标或基坐标 |
| `speed` | 运动速度参数 |
| `zone` | 到点/过渡参数 |
| `load` | 负载 |
| `robot_config` | 肩、肘、腕等机器人配置 |
| `ext_axis` | 外部轴 |
| `path` | 路径对象 |
| `operation` | 工艺操作对象 |

### 6.3 变量声明

```text
const int max_retry = 3
var int retry = 0
var bool part_ok = false
var pose_target p_tmp = pick offset z 100 mm
persistent real counter = 0
```

规则：

1. `const` 编译后不可修改。
2. `persistent` 在项目或控制器状态中持久化。
3. `var` 默认为局部变量。
4. 类型可以显式写出，自动生成程序建议显式写出。

## 7. 坐标、工具和目标点

### 7.1 工具

```text
persistent tool gripper = tool {
  tcp: pose(0 mm, 0 mm, 180 mm, 0 deg, 0 deg, 0 deg),
  mass: 2.5 kg,
  cog: [0 mm, 0 mm, 80 mm]
}
```

### 7.2 工件坐标

```text
persistent frame fixture = frame {
  origin: pose(800 mm, 0 mm, 200 mm, 0 deg, 0 deg, 0 deg)
}
```

### 7.3 关节目标点

```text
target home = joint_target {
  joints: [0 deg, -30 deg, 60 deg, 0 deg, 60 deg, 0 deg]
}
```

`joint_target` 主要用于 `movej`，后处理可映射到 ABB `jointtarget`、KUKA `E6AXIS` 或 FANUC 关节位置数据。

### 7.4 笛卡尔目标点

```text
target pick = pose_target {
  pose: pose(500 mm, 120 mm, 300 mm, 180 deg, 0 deg, 90 deg),
  config: robot_config(0, 0, 1),
  tool: gripper,
  frame: fixture
}
```

`pose()` 参数顺序为：

```text
pose(x, y, z, rx, ry, rz)
```

首版 `rx/ry/rz` 使用固定约定的欧拉角，建议内部立即转换为四元数。为了避免姿态歧义，也允许使用四元数：

```text
poseq(500 mm, 120 mm, 300 mm, 0, 0, 0.7071068, 0.7071068)
```

### 7.5 偏移目标点

```text
var pose_target approach = pick offset z 100 mm
var pose_target p2 = pick offset x 20 mm y -10 mm z 50 mm
```

偏移默认在目标点所属 `frame` 下解释。需要明确坐标系时：

```text
var pose_target p3 = pick offset_in tool z -50 mm
var pose_target p4 = pick offset_in frame fixture x 100 mm
```

## 8. 速度和过渡

### 8.1 速度

```text
const speed v_joint_fast = joint(80 %)
const speed v_joint_abs = joint(90 deg/s)
const speed v_pick = linear(300 mm/s)
const speed v_weld = linear(120 mm/s)
const speed v_orient = angular(90 deg/s)
```

运动语义：

1. `joint(...)` 用于关节运动速度。
2. `linear(...)` 用于 TCP 线速度。
3. `angular(...)` 可作为姿态插补角速度限制。
4. 加速度可作为可选参数：

```text
const speed v_slow = linear(100 mm/s, acc 500 mm/s2)
```

### 8.2 过渡

```text
const zone z_fine = fine
const zone z10 = z(10 mm)
const zone z50 = z(50 mm)
const zone z_cnt = cnt(30)
const zone z_cont = continuous
```

语义：

1. `fine`：精确到点，不做过渡。
2. `z(distance)`：以距离近似表示过渡半径，便于映射 ABB `zonedata` 和 KUKA `APO`。
3. `cnt(percent)`：以百分比表示连续过渡，便于映射 FANUC `CNT`。
4. `continuous`：允许连续通过，实际过渡由后处理器或虚拟控制器策略决定。

## 9. 运动指令

### 9.1 基本语法

```text
movej TargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
movel TargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
movec via ViaTargetExpr target EndTargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
```

示例：

```text
movej home speed joint(60 %) zone fine tool gripper frame fixture
movel pick speed linear(300 mm/s) zone z10
movec via arc_mid target arc_end speed linear(150 mm/s) zone fine
```

### 9.2 MOVEJ 语义

`movej` 表示关节空间运动。机器人按各轴关节角度差分运行，关节同起同停，TCP 轨迹不要求是直线。

编译规则：

1. 目标为 `joint_target` 时直接使用关节数组。
2. 目标为 `pose_target` 时先用 IK 求终点关节。
3. 轨迹按关节空间插补。
4. 后处理映射：
   - ABB：`MoveJ`
   - FANUC：`J P[...]`
   - KUKA：`PTP`

### 9.3 MOVEL 语义

`movel` 表示 TCP 直线运动。TCP 沿起点到终点的空间直线运行，关节角由每个 TCP 采样点 IK 求解。

编译规则：

1. 目标必须能解析为笛卡尔位姿。
2. 当前点作为直线起点。
3. 姿态按默认策略插补，首版推荐固定姿态或 slerp。
4. 后处理映射：
   - ABB：`MoveL`
   - FANUC：`L P[...]`
   - KUKA：`LIN`

### 9.4 MOVEC 语义

`movec` 表示 TCP 圆弧运动。TCP 从当前点出发，经过 via 点，到达 target 点，沿圆弧运行，关节角由每个圆弧采样点 IK 求解。

编译规则：

1. 起点为当前 TCP。
2. `via` 和 `target` 必须能解析为笛卡尔位姿。
3. 三点不能重合或近似共线。
4. 首版使用 via 点位置约束圆弧，姿态按起点到终点插补。
5. 后处理映射：
   - ABB：`MoveC`
   - FANUC：`C P[...]`
   - KUKA：`CIRC`

### 9.5 当前工具和当前坐标

```text
set_tool gripper
set_frame fixture
set_speed linear(300 mm/s)
set_zone z10
```

参数优先级：

1. 运动语句显式参数最高。
2. `path defaults` 次之。
3. 目标点自带 `tool/frame` 次之。
4. 当前控制器状态最低。

编译到 IR 时，所有运动必须有确定的 `tool`、`frame`、`speed`、`zone`。

## 10. Path 语法

Path 是自动离线编程的核心对象。CAD 曲线、规划点、示教点、工艺模板应优先生成 Path，而不是直接生成散乱运动语句。

### 10.1 基本语法

```text
path pick_path {
  defaults {
    tool: gripper,
    frame: fixture,
    speed: linear(300 mm/s),
    zone: z10
  }

  point approach movej home zone fine
  point p1 movel pick offset z 100 mm
  point p2 movel pick zone fine
}
```

### 10.2 圆弧点

```text
path arc_path {
  defaults {
    tool: torch,
    frame: part_frame,
    speed: linear(120 mm/s),
    zone: fine
  }

  point p0 movej start
  point p1 movec via mid target end
}
```

### 10.3 来源元数据

```text
path curve_032_generated {
  source {
    type: cad_curve
    id: "edge_032"
    sample_distance: 5 mm
    normal_strategy: surface_normal
  }

  defaults {
    tool: spray_gun,
    frame: part_frame,
    speed: linear(500 mm/s),
    zone: z20,
    posture: follow_normal
  }

  point p000 movel T_curve_032_000 zone fine
  point p001 movel T_curve_032_001
  point p002 movel T_curve_032_002
}
```

来源元数据用于：

1. 路径重采样。
2. 目标点重命名。
3. 重新生成程序。
4. 报告中定位到 CAD 曲线或工艺对象。

### 10.4 Path event

```text
event before p001 io.do[10] = true
event after p010 io.do[10] = false
event at p005 distance -20 mm pulse io.do[20] duration 100 ms
```

语义：

1. `before`：到达路径点之前执行。
2. `after`：完成路径点之后执行。
3. `at ... distance`：相对路径点提前或滞后触发，首版可编译为最近采样点事件。
4. 后处理到品牌程序时，若品牌不支持精确路径触发，应生成转换报告。

### 10.5 `run_path`

```text
proc main()
  run_path pick_path
end
```

编译语义：

1. 展开 Path 的 defaults、points、events。
2. 每个 point 变为 Motion IR。
3. event 变为 IO/Wait/Process IR。
4. 保留 source map，调试时可从程序行定位到路径点。

## 11. Operation 语法

Operation 表达工艺语义，比 Path 更高一层。

```text
operation weld_op_01 {
  kind: arc_welding
  path: weld_seam_01

  process {
    weld_id: "WELD_1"
    voltage: 24.0
    current: 180.0
    weave: none
  }

  start_action:
    io.do[20] = true

  end_action:
    io.do[20] = false
}
```

首版建议支持的 `kind`：

| kind | 用途 |
| --- | --- |
| `handling` | 搬运、上下料 |
| `arc_welding` | 弧焊 |
| `spot_welding` | 点焊 |
| `dispensing` | 涂胶 |
| `spraying` | 喷涂 |
| `grinding` | 打磨 |
| `cutting` | 切割 |
| `generic_path` | 通用路径 |

`run_operation`：

```text
proc main()
  run_operation weld_op_01
end
```

编译语义：

1. 执行 `start_action`。
2. 执行 operation 引用的 path。
3. 执行 `end_action`。
4. 保留工艺参数，供仿真 UI、报告和后处理使用。

## 12. IO 与等待

### 12.1 IO 地址

```text
io.di[1]
io.do[1]
io.ai[1]
io.ao[1]
io.gi[1]
io.go[1]
io.ri[1]
io.ro[1]
```

推荐通过 `io_map.json` 提供别名：

```text
io.alias.clamp_open
io.alias.clamp_closed
io.alias.gripper_close_cmd
```

### 12.2 IO 写入

```text
io.do[1] = true
io.go[1] = 16
io.ao[1] = 3.5
```

### 12.3 wait

```text
wait io.di[1] == true
wait io.alias.clamp_closed == true timeout 2 s
wait io.ai[2] > 3.5 timeout 1.5 s
wait all(io.di[1] == true, io.di[2] == false)
wait any(io.di[10] == true, timer.done("T_PICK"))
wait rising(io.di[4]) timeout 5 s
wait falling(io.di[5]) timeout 5 s
```

超时处理：

```text
wait io.di[1] == true timeout 2 s on_timeout alarm "Clamp close timeout"
wait io.di[1] == true timeout 2 s on_timeout call recover_clamp()
```

### 12.4 pulse

```text
pulse io.do[3] duration 200 ms
```

虚拟控制器语义：

1. 立即置位输出。
2. 虚拟时间到达 duration 后自动复位。
3. trace 中必须记录置位和复位事件。

## 13. 流程控制

流程控制用于表达工艺分支、重试、配方选择、批量工位循环和异常恢复。GRL 新程序应优先使用结构化控制流；`label/jump` 主要用于兼容 FANUC TP/LS 等已有程序导入。

### 13.1 条件 `if / elseif / else`

```text
if part_ok
  run_path good_path
elseif retry < 3
  call retry_pick()
else
  alarm "Pick failed"
end
```

语义规则：

1. `if` 条件表达式必须能转换为 `bool`。
2. `elseif` 可以出现零次或多次。
3. `else` 最多出现一次，且必须位于所有 `elseif` 之后。
4. 条件按顺序求值，命中第一条为 true 的分支后，不再执行后续分支。
5. 每个 `if` 必须以 `end` 结束。
6. 条件表达式允许访问变量、IO、函数返回值和比较表达式。
7. 运动指令允许出现在任意分支内，但编译器必须保证每条分支中的工具、坐标系、速度和 zone 可解析。

常用条件表达式：

```text
if io.di[1] == true
if count >= 3 and not part_ok
if is_part_ready()
if recipe_id == 2 or recipe_id == 3
```

### 13.2 `while` 循环

```text
while retry < 3
  call try_pick()
  if part_ok
    break
  end
  retry = retry + 1
end
```

语义规则：

1. `while` 条件必须为 `bool`。
2. 每次进入循环前求值。
3. `break` 立即退出当前循环。
4. `continue` 跳过本轮剩余语句，进入下一轮条件判断。
5. 编译器建议对可能无限循环的 `while true` 给出 warning，除非循环体内包含明确的 `break`、`return`、`raise` 或 `wait`。

### 13.3 `for` 循环

```text
for i = 0 to 9 step 1
  call process_slot(i)
end
```

也允许省略 `step`，默认步长为 `1`：

```text
for layer = 1 to layer_count
  call weld_layer(layer)
end
```

反向循环：

```text
for i = 10 to 0 step -1
  call clear_slot(i)
end
```

语义规则：

1. 循环变量默认是当前 `for` 块局部变量。
2. 起点、终点、步长必须是整数或可安全转换为整数。
3. `step` 不能为 `0`。
4. `break` 和 `continue` 只影响当前最近一层循环。
5. 后处理到不支持结构化 `for` 的品牌时，可展开为标签和跳转，但必须保持 source map。

### 13.4 `switch / case / default`

```text
switch recipe_id
  case 1
    run_operation op_a
  case 2
    run_operation op_b
  default
    alarm "Unknown recipe"
end
```

语义规则：

1. `switch` 表达式可以是 `int`、`bool`、`string` 或枚举型。
2. `case` 值必须是常量表达式。
3. 每个 `case` 默认不向下贯穿，不需要写 `break`。
4. `default` 最多出现一次。
5. 若需要多个值进入同一分支，可以使用逗号：

```text
switch recipe_id
  case 1, 2, 3
    run_operation common_recipe
  case 10
    run_operation special_recipe
  default
    alarm "Unknown recipe"
end
```

6. 导出到 FANUC TP/LS 时，`switch` 可转换为 `SELECT`、条件跳转或 `LBL/JMP` 组合。
7. 导出到 ABB/KUKA 时，优先转换为品牌结构化分支。

### 13.5 `break` 和 `continue`

```text
while true
  call check_part()
  if part_ok
    break
  end
  retry = retry + 1
  if retry < max_retry
    continue
  end
  alarm "Retry failed"
  break
end
```

规则：

1. `break` 只能出现在 `while`、`for` 或 `switch` 中。
2. `continue` 只能出现在 `while` 或 `for` 中。
3. 在 `switch` 中不需要 `break` 防止贯穿，因为 GRL 默认不贯穿。

### 13.6 label 和 jump

```text
label retry
call try_pick()
if not part_ok
  jump retry
end
```

新程序不建议优先使用 `label/jump`，但必须支持它，因为 FANUC TP/LS 程序常用 `LBL/JMP`，导入时需要映射。

`label/jump` 规则：

1. `label` 名称在当前 `proc` 内唯一。
2. `jump` 只能跳转到当前 `proc` 内的 label。
3. 禁止 `jump` 进入另一个 `if/while/for/switch/try` 块内部。
4. 允许 `jump` 跳出块结构，但编译器应生成 warning。
5. 自动生成的新程序不应使用 `label/jump`，除非目标后处理配置要求 FANUC 风格输出。

## 14. 子程序和函数

### 14.1 `proc` 子程序

`proc` 是无返回值子程序，用于组织工艺步骤、运动流程、IO 流程和恢复逻辑。

```text
proc pick_part(int slot)
  movej home speed joint(60 %) zone fine
  run_path pick_path
end
```

调用：

```text
call pick_part(1)
call pick_part(slot_id)
```

语义规则：

1. `proc` 可以执行运动、IO、wait、run_path、run_operation。
2. `proc` 可以调用其他 `proc`。
3. 默认不允许无限递归。编译器应检测直接递归和明显的间接递归，并给出 warning 或 error。
4. `return` 可以提前退出 `proc`，但不能携带返回值。
5. `proc main()` 是默认入口。一个模块中最多一个默认入口。

### 14.2 参数方向

参数支持 `in`、`out`、`inout` 三种方向。未写方向时默认为 `in`。

```text
proc pick_part(in int slot)
  call move_to_slot(slot)
end

proc read_part(out bool ok)
  ok = io.di[1]
end

proc increment_retry(inout int retry)
  retry = retry + 1
end
```

规则：

1. `in` 参数按值传入，子程序内修改不影响调用者。
2. `out` 参数调用前不要求有有效值，子程序必须在所有正常返回路径上赋值。
3. `inout` 参数按引用语义传入，子程序内修改会回写调用者。
4. `out` 和 `inout` 实参必须是可赋值左值，不能是字面量或表达式。
5. 后处理到不支持显式参数方向的品牌时，可生成临时变量、寄存器或转换报告。

调用示例：

```text
var bool ok = false
var int retry = 0

call read_part(ok)
call increment_retry(retry)
```

### 14.3 `func` 函数

`func` 有返回值，适合计算条件、选择目标点、计算偏移、读取配方参数。函数应尽量无副作用。

```text
func bool is_part_ready()
  return io.di[1] == true and io.di[2] == false
end
```

带参数函数：

```text
func pose_target slot_pose(int slot)
  return base_pick offset x (slot * 50 mm)
end

func bool can_retry(int retry, int max_retry)
  return retry < max_retry
end
```

调用方式：

```text
if can_retry(retry, max_retry)
  call retry_pick()
end

movel slot_pose(slot_id) speed linear(200 mm/s) zone z10
```

规则：

1. `func` 必须声明返回类型。
2. 所有正常返回路径必须返回兼容类型的值。
3. `func` 允许调用其他 `func`。
4. `func` 默认不允许执行运动指令、`wait`、`pulse`、`run_path`、`run_operation`。编译器应把这类用法作为 error 或强 warning。
5. 如果确实需要带副作用的逻辑，应使用 `proc`。

### 14.4 作用域和名称解析

名称解析顺序：

1. 当前块局部变量。
2. 当前 `proc/func` 参数。
3. 当前模块中的 `const/var/persistent/target/path/operation/proc/func`。
4. `import` 模块导出的符号。
5. 内置函数和内置类型。

规则：

1. 内层局部变量允许遮蔽外层变量，但编译器应对同名遮蔽给出 warning。
2. 不允许局部变量与同一作用域内的 `proc/func/target/path/operation` 重名。
3. `for` 循环变量只在循环体内有效。
4. `label` 只在当前 `proc` 内有效。

### 14.5 子程序调用和后处理

后处理映射：

| GRL | ABB RAPID | FANUC LS/TP | KUKA KRL |
| --- | --- | --- | --- |
| `proc name()` | `PROC name()` | `/PROG NAME` 或子程序 | `DEF name()` |
| `call sub()` | `sub;` 或 `sub()` | `CALL SUB` | `sub()` |
| `return` | `RETURN` | `END`/跳转近似 | `RETURN` |
| `out/inout` 参数 | `VAR` 参数或数据变量 | 寄存器/PR/临时变量近似 | 参数或全局变量近似 |

如果目标品牌或输出格式不支持某种参数模式，后处理器必须生成转换报告，不能静默丢失语义。

## 15. 异常、报警和中断

### 15.1 alarm 和 raise

```text
alarm "Part not detected"
raise E_PICK_FAILED
```

### 15.2 try/catch

```text
try
  run_operation pick_op
catch E_PICK_FAILED
  call recover_pick()
finally
  io.do[1] = false
end
```

首版可先实现 `alarm`、`raise` 和简单 `catch`，复杂品牌错误恢复分阶段支持。

### 15.3 trap 和 interrupt

```text
trap emergency_stop()
  stop_motion
  io.do[100] = false
  alarm "Emergency stop input"
end

interrupt e_stop when io.di[100] == true call emergency_stop()
enable interrupt e_stop
disable interrupt e_stop
```

后处理映射：

1. ABB 可映射到 `TRAP` 和 interrupt 机制。
2. KUKA 可映射到 `INTERRUPT DECL`、`INTERRUPT ON/OFF` 等近似结构。
3. FANUC 首版可生成后台逻辑、条件监控或转换报告。

## 16. 多任务

首版只定义语法，不要求完整实现多任务实时调度：

```text
task background monitor_io cycle 20 ms
  if io.di[99] == true
    alarm "Safety signal lost"
  end
end
```

虚拟控制器可先按周期任务模拟；后处理时根据品牌能力生成后台任务或转换报告。

## 17. 品牌扩展和后处理提示

### 17.1 post_hint

```text
post_hint abb {
  module_name: "MainModule"
  use_wobj: true
}

post_hint fanuc {
  program_name: "MAIN"
  default_uframe: 1
  default_utool: 1
}

post_hint kuka {
  src_name: "MAIN"
  dat_name: "MAIN"
  advance: 3
}
```

### 17.2 brand metadata

```text
@brand.abb {
  conf_l: true
}

@brand.fanuc {
  group: 1
  cnt: 50
}

@brand.kuka {
  c_dis: true
}
```

规则：

1. `@brand.*` 只影响指定品牌后处理。
2. 通用虚拟控制器不应依赖品牌 metadata 才能执行。
3. 无法支持的品牌扩展必须进入转换报告。

## 18. 自动编程生成规则

自动生成 GRL 时，应遵循以下规则：

1. 优先生成 `target`、`path`、`operation`，不要直接把大量运动语句塞进 `proc main()`。
2. 目标点命名稳定，推荐 `T_{path}_{index}` 或语义名称，例如 `T_PICK_APPROACH`。
3. 路径点命名稳定，推荐 `p000`、`p001`、`p002`。
4. 路径整体参数放入 `defaults`，单点差异写在 point 上。
5. 接近点、工艺开始点、工艺结束点、离开点必须显式标注。
6. IO、夹具、焊接开关、喷涂开关应生成 path event 或 operation action。
7. CAD 来源、曲线 ID、采样距离、法向策略必须写入 `source`。
8. 自动生成文本必须格式化稳定，同一输入重复生成结果一致。
9. 编译器应能把 GRL 再解析回等价 OLP 对象模型。
10. 后处理器应能选择 `compact` 或 `expanded` 输出风格。

生成风格：

```text
// compact
proc main()
  run_operation weld_op_01
end

// expanded
proc main()
  movel T001 speed linear(120 mm/s) zone z5 tool torch frame part_frame
  movel T002 speed linear(120 mm/s) zone z5 tool torch frame part_frame
end
```

## 19. 后处理映射规则

### 19.1 ABB RAPID

| GRL | ABB RAPID |
| --- | --- |
| `module` | `MODULE` |
| `proc` | `PROC` |
| `movej` | `MoveJ` |
| `movel` | `MoveL` |
| `movec` | `MoveC` |
| `pose_target` | `robtarget` |
| `joint_target` | `jointtarget` |
| `tool` | `tooldata` |
| `frame` | `wobjdata` |
| `linear(300 mm/s)` | `v300` 或自定义 `speeddata` |
| `fine`、`z(10 mm)` | `fine`、`z10` 或自定义 `zonedata` |
| `io.do[1] = true` | `SetDO do1, 1` |
| `wait io.di[1] == true` | `WaitDI di1, 1` |

示例：

```text
movel pick speed linear(300 mm/s) zone z10 tool gripper frame fixture
```

可后处理为：

```text
MoveL pick, v300, z10, gripper\WObj:=fixture;
```

### 19.2 FANUC LS/TP 风格

| GRL | FANUC |
| --- | --- |
| `proc main()` | `/PROG MAIN` |
| `movej` | `J P[...]` |
| `movel` | `L P[...]` |
| `movec` | `C P[...]` |
| `pose_target` | `P[...]` 或 `PR[...]` |
| `tool` | UTOOL |
| `frame` | UFRAME |
| `joint(50 %)` | `50%` |
| `linear(300 mm/s)` | `300mm/sec` |
| `fine` | `FINE` |
| `cnt(50)` | `CNT50` |
| `io.do[1] = true` | `DO[1]=ON` |
| `wait io.di[1] == true` | `WAIT DI[1]=ON` |

示例：

```text
movel P10 speed linear(300 mm/s) zone cnt(10)
```

可后处理为：

```text
L P[10] 300mm/sec CNT10 ;
```

### 19.3 KUKA KRL

| GRL | KUKA KRL |
| --- | --- |
| `proc main()` | `DEF MAIN()` |
| `movej` | `PTP` |
| `movel` | `LIN` |
| `movec` | `CIRC` |
| `pose_target` | `E6POS` |
| `joint_target` | `E6AXIS` |
| `tool` | `$TOOL` |
| `frame` | `$BASE` |
| `linear(300 mm/s)` | `$VEL.CP = 0.3` |
| `fine` | no approximation |
| `continuous`、`z(...)` | `C_DIS` 或 `$APO` |
| `io.do[1] = true` | `$OUT[1] = TRUE` |
| `wait io.di[1] == true` | `WAIT FOR $IN[1] == TRUE` |

示例：

```text
movel XPICK speed linear(300 mm/s) zone continuous
```

可后处理为：

```text
$VEL.CP = 0.3
LIN XPICK C_DIS
```

## 20. 编译语义和 IR

编译管线：

```text
GRL Source
  -> Lexer
  -> Parser
  -> AST
  -> Symbol Table
  -> Semantic Analyzer
  -> Executable IR
  -> Virtual Controller
  -> Post Processor
```

运动 IR 示例：

```ts
interface MotionInstruction {
  kind: "motion";
  motionType: "joint" | "linear" | "circular";
  target: TargetRef | ResolvedTarget;
  via?: TargetRef | ResolvedTarget;
  speed: SpeedSpec;
  zone: ZoneSpec;
  tool: ToolRef;
  frame: FrameRef;
  sourceRange: SourceRange;
  sourceObject?: {
    pathId?: string;
    pointId?: string;
    operationId?: string;
  };
  brandMeta?: Record<string, unknown>;
}
```

## 21. 语义检查

编译器必须检查：

1. 标识符是否重复或未声明。
2. 类型是否匹配。
3. 目标点类型是否适合运动指令。
4. `movec` 是否缺少 via 点。
5. 圆弧三点是否重合或共线。
6. 工具、坐标系、速度、过渡是否可解析。
7. 单位是否正确。
8. IO 地址是否存在于 IO map 或允许范围。
9. 子程序参数数量和类型是否匹配。
10. `out` 参数是否在所有正常返回路径上赋值。
11. `inout` 和 `out` 实参是否为可赋值左值。
12. `func` 是否在所有正常返回路径上返回正确类型。
13. `break`、`continue` 是否出现在合法结构内。
14. `switch case` 是否为常量表达式，是否存在重复 case。
15. `jump` 是否跳入非法块结构。
16. `wait` 是否有可执行条件。
17. Path 是否为空。
18. Path point 名称是否重复。
19. Operation 是否引用不存在的 Path。
20. 目标点是否可达。
21. 关节是否超限。
22. 后处理目标品牌是否支持所用语义。

诊断格式建议：

```text
E1001: target 'pick' is not defined
E2003: movec points are collinear: start=p0, via=p1, target=p2
W3002: zone z50 is larger than segment length
W4001: KUKA post approximates GRL cnt(30) by C_DIS
```

## 22. EBNF 语法草案

以下是首版解析器可采用的简化 EBNF：

```text
program        = [ language_decl ] module_decl ;
language_decl  = "language" "grl" version ;
module_decl    = "module" identifier { top_decl } "end" ;

top_decl       = import_decl
               | data_decl
               | target_decl
               | path_decl
               | operation_decl
               | proc_decl
               | func_decl
               | trap_decl
               | task_decl
               | post_hint_decl ;

import_decl    = "import" identifier ;
data_decl      = ( "persistent" | "const" | "var" ) type identifier "=" expr ;
target_decl    = "target" identifier "=" ( joint_target | pose_target ) ;

joint_target   = "joint_target" "{" "joints" ":" array [ "," ext_axis ] "}" ;
pose_target    = "pose_target" "{" "pose" ":" pose_expr
                 [ "," "config" ":" config_expr ]
                 [ "," "tool" ":" identifier ]
                 [ "," "frame" ":" identifier ] "}" ;

path_decl      = "path" identifier "{" { path_item } "}" ;
path_item      = defaults_block | source_block | path_point | path_event ;
defaults_block = "defaults" "{" { property } "}" ;
source_block   = "source" "{" { property } "}" ;
path_point     = "point" identifier motion_stmt ;
path_event     = "event" ( "before" | "after" ) identifier statement ;

operation_decl = "operation" identifier "{"
                 "kind" ":" identifier
                 "path" ":" identifier
                 [ process_block ]
                 [ action_block ]
                 "}" ;

proc_decl      = "proc" identifier "(" [ param_list ] ")" { statement } "end" ;
func_decl      = "func" type identifier "(" [ param_list ] ")" { statement } "end" ;
trap_decl      = "trap" identifier "(" ")" { statement } "end" ;

statement      = motion_stmt
               | run_stmt
               | assign_stmt
               | wait_stmt
               | pulse_stmt
               | if_stmt
               | while_stmt
               | for_stmt
               | switch_stmt
               | call_stmt
               | return_stmt
               | break_stmt
               | continue_stmt
               | label_stmt
               | jump_stmt
               | alarm_stmt
               | try_stmt ;

motion_stmt    = movej_stmt | movel_stmt | movec_stmt ;
target_expr    = target_ref [ offset_clause ] | pose_expr ;
offset_clause  = "offset" { axis length }
               | "offset_in" ( "tool" | "frame" identifier ) { axis length } ;
movej_stmt     = "movej" target_expr motion_params ;
movel_stmt     = "movel" target_expr motion_params ;
movec_stmt     = "movec" "via" target_expr "target" target_expr motion_params ;

motion_params  = [ "speed" speed_expr ] [ "zone" zone_expr ]
                 [ "tool" identifier ] [ "frame" identifier ] ;

run_stmt       = "run_path" identifier | "run_operation" identifier ;
wait_stmt      = "wait" expr [ "timeout" duration ] [ "on_timeout" timeout_action ] ;
pulse_stmt     = "pulse" io_ref "duration" duration ;
assign_stmt    = lvalue "=" expr ;
call_stmt      = "call" identifier "(" [ arg_list ] ")" ;
return_stmt    = "return" [ expr ] ;
break_stmt     = "break" ;
continue_stmt  = "continue" ;

if_stmt        = "if" expr { statement }
                 { "elseif" expr { statement } }
                 [ "else" { statement } ]
                 "end" ;

while_stmt     = "while" expr { statement } "end" ;
for_stmt       = "for" identifier "=" expr "to" expr [ "step" expr ] { statement } "end" ;

switch_stmt    = "switch" expr { case_clause } [ default_clause ] "end" ;
case_clause    = "case" const_expr { "," const_expr } { statement } ;
default_clause = "default" { statement } ;

label_stmt     = "label" identifier ;
jump_stmt      = "jump" identifier ;

param_list     = param { "," param } ;
param          = [ "in" | "out" | "inout" ] type identifier ;
arg_list       = expr { "," expr } ;

expr           = literal
               | identifier
               | call_expr
               | io_ref
               | unary_expr
               | binary_expr
               | "(" expr ")" ;
call_expr      = identifier "(" [ arg_list ] ")" ;
```

## 23. 完整示例

```text
language grl 0.1

module Main

  persistent tool gripper = tool {
    tcp: pose(0 mm, 0 mm, 180 mm, 0 deg, 0 deg, 0 deg),
    mass: 2.5 kg
  }

  persistent frame fixture = frame {
    origin: pose(800 mm, 0 mm, 200 mm, 0 deg, 0 deg, 0 deg)
  }

  const speed v_fast = joint(70 %)
  const speed v_pick = linear(300 mm/s)
  const zone z_pick = z(10 mm)

  target home = joint_target {
    joints: [0 deg, -30 deg, 60 deg, 0 deg, 60 deg, 0 deg]
  }

  target pick = pose_target {
    pose: pose(500 mm, 120 mm, 300 mm, 180 deg, 0 deg, 90 deg),
    config: robot_config(0, 0, 1),
    tool: gripper,
    frame: fixture
  }

  target place = pose_target {
    pose: pose(650 mm, -100 mm, 320 mm, 180 deg, 0 deg, 90 deg),
    config: robot_config(0, 0, 1),
    tool: gripper,
    frame: fixture
  }

  path pick_path {
    defaults {
      tool: gripper,
      frame: fixture,
      speed: v_pick,
      zone: z_pick
    }

    point approach movej home speed v_fast zone fine
    point above_pick movel pick offset z 100 mm
    point at_pick movel pick zone fine
    event after at_pick io.do[1] = true
    event after at_pick wait io.di[1] == true timeout 2 s on_timeout alarm "Clamp close timeout"
    point leave_pick movel pick offset z 100 mm
  }

  path place_path {
    defaults {
      tool: gripper,
      frame: fixture,
      speed: v_pick,
      zone: z_pick
    }

    point above_place movel place offset z 100 mm
    point at_place movel place zone fine
    event after at_place io.do[1] = false
    event after at_place wait io.di[2] == true timeout 2 s on_timeout alarm "Clamp open timeout"
    point leave_place movel place offset z 100 mm
  }

  operation pick_op {
    kind: handling
    path: pick_path
    process {
      gripper: "main_clamp"
    }
  }

  operation place_op {
    kind: handling
    path: place_path
    process {
      gripper: "main_clamp"
    }
  }

  proc main()
    set_tool gripper
    set_frame fixture

    run_operation pick_op
    run_operation place_op

    movej home speed v_fast zone fine
  end

end
```

## 24. 实施优先级

P0 必须实现：

1. `language`、`module`、`proc`。
2. `const`、`var`、`persistent`。
3. `tool`、`frame`、`speed`、`zone`。
4. `joint_target`、`pose_target`。
5. `movej`、`movel`、`movec`。
6. `path`、`point`、`event`、`run_path`。
7. `operation`、`run_operation`。
8. `if`、`elseif`、`else`、`while`、`for`、`switch`。
9. `call`、`return`、`break`、`continue`。
10. `proc` 参数方向 `in/out/inout`。
11. `func`、函数调用表达式和返回值检查。
12. `io.do/di`、`wait`、`pulse`。
13. AST、语义检查、IR、source map。
14. ABB、FANUC、KUKA 后处理原型。

P1 扩展：

1. `label/jump` 和品牌标签程序导入。
2. `trap`、`interrupt`。
3. 多任务 `task`。
4. 更完整的 IO 类型。
5. 工艺模板库。
6. 品牌程序导入。
7. 复杂品牌扩展和转换报告。

## 25. 参考资料

1. ABB RAPID Technical Reference Manual，RAPID Instructions, Functions and Data Types：  
   https://library.e.abb.com/public/b227fcd260204c4dbeb8a58f8002fe64/Rapid_instructions.pdf

2. ABB RAPID Overview：  
   https://search.abb.com/library/Download.aspx?Action=Launch&DocumentID=3HAC050947-001&DocumentPartId=&LanguageCode=en

3. KUKA System Software，说明 KSS 支持 InLine forms 和 KRL 专家编程：  
   https://www.kuka.com/de-de/produkte-leistungen/robotersysteme/software/systemsoftware/kuka_systemsoftware

4. KUKA Application and Robot Programming，说明 KRL、Sunrise programming、simulation/offline programming 等工作流：  
   https://www.kuka.com/en-de/services/service_robots-and-machines/installation-start-up-and-programming-of-robots/application-and-robot-programming

5. FANUC PLC Motion Interface，说明 FANUC 可从上位控制发起 linear、joint、circular robot motion，并管理 speeds 与 termination types：  
   https://www.fanucamerica.com/products/software/plc-motion-interface

6. FANUC ROBOGUIDE，说明 FANUC 离线编程可生成机器人程序，并支持 CAD to Path、仿真和路径开发：  
   https://www.fanucamerica.com/products/software/roboguide

7. FANUC ASCII Program Loader，说明 FANUC LS 可读程序可编译为 TP 程序：  
   https://www.fanucamerica.com/products/controller-series/r-50ia
