marqdo

lib/外联 — 外联内核 v2ok

stdlib/外联.mq.md

文档

外联:lib/外联.mq.md

main

说明

lib/外联 用本机解释器运行代码围栏和内联源码。v2 不改 Markdown 表面,新增结构化记录、JSON 进出、并行扇出、Python/Node 持久内核和显式状态桥。

文件仍然是一篇文档:绑定的代码围栏是可读的 Markdown,外联.* 把它变成可执行单元。

---

先探测,再运行。

node = > 外联.探测 语言="node"
> 打印 内容 =okaccess(`node`, "ok")
> 打印 内容 =versionaccess(`node`, "version")

探测 返回 {lang, cmd, path, ok, version, error}。省略语言时探测所有已知语言。

---

结构化记录,而不只是 stdout。

code 标记 node
console.log("来自外联内核的问候");
来自外联内核的问候


— 20ms
记录 = > 外联.执行 代码=`标记`
> 打印 内容 =okaccess(`记录`, "ok")
> 打印 内容 =stdoutaccess(`记录`, "stdout")
> 打印 内容 =codeaccess(`记录`, "code")

执行 在进程失败时不抛错,而是在一条记录里给出 ok、code、stdout、stderr、ms、timed_out、truncated、result 与 error。

---

JSON 进,JSON 出。

code 双倍 node
let input = "";
process.stdin.on("data", chunk => { input += chunk; });
process.stdin.on("end", () => {
  console.log(JSON.stringify(JSON.parse(input) * 2));
});
42


— 30ms
单个 = > 外联.调用 代码=`双倍` 输入=21
> 打印 内容=单个

输入编码成 JSON 写到 stdin;stdout 的最后一个值再解析成 JSON。想同时保留原始记录时,用 执行 的 输出="json"。

---

并行扇出。

数值表 =
数值
1
2
3
4
翻倍 = > 外联.批量调用 代码=`双倍` 输入表=`数值表` 并发=4
> 打印 内容=翻倍

批量调用 对每个输入独立运行同一程序,保持输入顺序并返回解析后的 JSON 列表;批量运行 对完整 invoke 选项表做同样的事。

---

持久内核。

会话 = > 外联.会话开启 语言="node"
第一次 = > 外联.会话求值 标识=`会话` 源码="let total = 0; for (let i = 1; i <= 5; i += 1) total += i;"
第二次 = > 外联.会话求值 标识=`会话` 源码="total * 2"
> 打印 内容 =resultaccess(`第二次`, "result")

第一次求值创建的变量会保留到第二次。内核不是 shell:用 会话求值、会话取值、会话变量,最后 会话关闭。

---

显式状态桥。

> 外联.会话设值 标识=`会话` 名称="rate" 值=0.08
利率 = > 外联.会话取值 标识=`会话` 名称="rate"
> 打印 内容=利率
> 外联.会话关闭 标识=`会话`

这是 JSON 拷贝,不是共享内存:内核收到一个值,Marqdo 取回一个值。没有隐式镜像,也没有隐藏的全局命名空间。

---

view 中的单元。

foreign.session 拥有文档的内核状态。内核和单元顺序都是作为参数传入的普通表格,名字可以自由选择:

code text
`分析内核` =

| 名称 | 语言 | 命令 | 说明 |
|------|------|------|------|
| 分析 | python | | 共享分析 |
| 复核 | python | | 独立复核 |

`分析单元` =

| 单元 | 内核 | 说明 |
|------|------|------|
| 准备数据 | 分析 | 写入状态 |
| 计算指标 | 分析 | 读取状态 |
| 独立复核 | 复核 | 隔离内核 |

`会话` = > foreign.session kernels=`分析内核` cells=`分析单元`

view 消费 会话 句柄,提供 Run All、Step、切换内核、journal 和单元状态。没有 会话 时,view 的 cells 是无状态的一次性运行:同一套外壳,但没有持久内核。没有第二个 CLI 模式。完整 Python 示例见 features/19-外联视图单元.mq.md。

---

安全默认值。

边界默认
执行需要宿主执行能力
Shell不经过 shell;参数显式切分
输出有界,带 truncated
超时Unix 杀进程组 / Windows taskkill /T
工作目录文件沙箱根目录;不隐式越界
并行扇出有上限;每个任务隔离
Functions
Variables
  • description text
    结构化执行、JSON 调用、并行扇出、持久内核与显式状态桥
  • markup text
    occlusion
  • node map · 6
    langnode
    cmdnode -v
    path/usr/local/bin/node
    okTrue
    versionv22.23.4
    error
  • stdin int
    21
  • title text
    lib/外联 — 外联内核 v2
  • 会话 handle
    <handle>
  • 利率 num
    0.08
  • 单个 int
    42
  • 双倍 code · node
    ```node let input = ""; process.stdin.on("data", chunk => { input += chunk; }); process.stdin.on("end", () => { console.log(JSON.stringify(JSON.parse(input) * 2)); }); ```
  • 数值表 list · 4
    #value
    11
    22
    33
    44
  • 标记 code · node
    ```node console.log("来自外联内核的问候"); ```
  • 第一次 map · 16
    okTrue
    code0
    stdout
    stderr
    ms20
    langnode
    …
  • 第二次 map · 16
    okTrue
    code0
    stdout
    stderr
    ms0
    langnode
    …
  • 翻倍 list · 4
    #value
    12
    24
    36
    48
  • 记录 map · 14
    okTrue
    code0
    stdout来自外联内核的问候
    stderr
    ms20
    langnode
    …
可执行知识(辅助)

运行与结果

True v22.23.4 True 来自外联内核的问候 0 42 [2, 4, 6, 8] 30 0.08
完整源码
---
markup: occlusion
title: lib/外联 — 外联内核 v2
description: 结构化执行、JSON 调用、并行扇出、持久内核与显式状态桥
导入:
  外联: lib/外联.mq.md
stdin: 21
---

# main

`lib/外联` 用本机解释器运行代码围栏和内联源码。v2 不改 Markdown 表面,新增结构化记录、JSON 进出、并行扇出、Python/Node 持久内核和显式状态桥。

文件仍然是一篇文档:绑定的代码围栏是可读的 Markdown,`外联.*` 把它变成可执行单元。

---

先探测,再运行。

**node = > 外联.探测 语言="node"**

> 打印 内容=[ok](access(`node`, "ok"))
> 打印 内容=[version](access(`node`, "version"))

`探测` 返回 `{lang, cmd, path, ok, version, error}`。省略语言时探测所有已知语言。

---

结构化记录,而不只是 stdout。

`标记` =
```node
console.log("来自外联内核的问候");
```

**记录 = > 外联.执行 代码=`标记`**

> 打印 内容=[ok](access(`记录`, "ok"))
> 打印 内容=[stdout](access(`记录`, "stdout"))
> 打印 内容=[code](access(`记录`, "code"))

`执行` 在进程失败时不抛错,而是在一条记录里给出 `ok`、`code`、`stdout`、`stderr`、`ms`、`timed_out`、`truncated`、`result` 与 `error`。

---

JSON 进,JSON 出。

`双倍` =
```node
let input = "";
process.stdin.on("data", chunk => { input += chunk; });
process.stdin.on("end", () => {
  console.log(JSON.stringify(JSON.parse(input) * 2));
});
```

**单个 = > 外联.调用 代码=`双倍` 输入=21**

> 打印 内容=`单个`

输入编码成 JSON 写到 stdin;stdout 的最后一个值再解析成 JSON。想同时保留原始记录时,用 `执行` 的 `输出="json"`。

---

并行扇出。

`数值表` =

| 数值 |
|------|
| 1 |
| 2 |
| 3 |
| 4 |

**翻倍 = > 外联.批量调用 代码=`双倍` 输入表=`数值表` 并发=4**

> 打印 内容=`翻倍`

`批量调用` 对每个输入独立运行同一程序,保持输入顺序并返回解析后的 JSON 列表;`批量运行` 对完整 invoke 选项表做同样的事。

---

持久内核。

**会话 = > 外联.会话开启 语言="node"**

**第一次 = > 外联.会话求值 标识=`会话` 源码="let total = 0; for (let i = 1; i <= 5; i += 1) total += i;"**

**第二次 = > 外联.会话求值 标识=`会话` 源码="total * 2"**

> 打印 内容=[result](access(`第二次`, "result"))

第一次求值创建的变量会保留到第二次。内核不是 shell:用 `会话求值`、`会话取值`、`会话变量`,最后 `会话关闭`。

---

显式状态桥。

**> 外联.会话设值 标识=`会话` 名称="rate" 值=0.08**

**利率 = > 外联.会话取值 标识=`会话` 名称="rate"**

> 打印 内容=`利率`

**> 外联.会话关闭 标识=`会话`**

这是 JSON 拷贝,不是共享内存:内核收到一个值,Marqdo 取回一个值。没有隐式镜像,也没有隐藏的全局命名空间。

---

view 中的单元。

`foreign.session` 拥有文档的内核状态。内核和单元顺序都是作为参数传入的普通表格,名字可以自由选择:

```text
`分析内核` =

| 名称 | 语言 | 命令 | 说明 |
|------|------|------|------|
| 分析 | python | | 共享分析 |
| 复核 | python | | 独立复核 |

`分析单元` =

| 单元 | 内核 | 说明 |
|------|------|------|
| 准备数据 | 分析 | 写入状态 |
| 计算指标 | 分析 | 读取状态 |
| 独立复核 | 复核 | 隔离内核 |

`会话` = > foreign.session kernels=`分析内核` cells=`分析单元`
```

`view` 消费 `会话` 句柄,提供 Run All、Step、切换内核、journal 和单元状态。没有 `会话` 时,view 的 cells 是无状态的一次性运行:同一套外壳,但没有持久内核。没有第二个 CLI 模式。完整 Python 示例见 [`features/19-外联视图单元.mq.md`](../features/19-外联视图单元.mq.md)。

---

安全默认值。

| 边界 | 默认 |
|---|---|
| 执行 | 需要宿主执行能力 |
| Shell | 不经过 shell;参数显式切分 |
| 输出 | 有界,带 truncated |
| 超时 | Unix 杀进程组 / Windows taskkill /T |
| 工作目录 | 文件沙箱根目录;不隐式越界 |
| 并行 | 扇出有上限;每个任务隔离 |

完整 API 见 [../../lib/外联.mq.md](../../lib/外联.mq.md),设计见 [../../doc/design/foreign-kernel.md](../../doc/design/foreign-kernel.md)。

Variable