Show HN: E-- – 一种介于英语和Python之间的语言

Hacker News Top 工具

摘要

E-- 是一种使用规范英语语法并确定性编译为Python的编程语言,旨在结合LLM的创造力与可重现的运行时行为。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/16 01:47

frmoded/e–

来源: https://github.com/frmoded/e–

E– (English–)

一种用纯规范英语编写、并能确定性编译为 Python 的编程语言。

E–(“English–”)是消除歧义的英语:一种封闭的语法和固定词汇表,每种结构只有一种规范表述。它的目标是在保持像英语一样可读、可编辑的同时,可靠地编译为普通的、可复现的 Python。

为什么存在

LLM 生成的代码在运行时存在不确定性:不可复现、每次调用成本高、难以调试。E– 将 LLM 的角色与执行分离——LLM(可选)在编写时生成规范 E–;一个确定性解析器将 E– 编译为 Python;运行时保持纯净。两全其美:需要时利用 LLM 的创造力,之后始终拥有确定性行为。

快速开始

从 PyPI 安装:

pip install e-minus-minus

将规范的 E– 文件转译成 Python:

emm-transpile examples/describe.emm

就这样。对于没有 {{ }} 槽的规范 E– 不需要 LLM。更多信息请参见下面的 “运行 E–”,以及 “解析 {{ }} 槽” 了解在使用自由英语输入或值槽时的 LLM 设置。LLM 路径是可选的,位于 [llm] 额外选项中:pip install "e-minus-minus[llm]"

自己开发 E–?克隆仓库,并在工作树位于 PYTHONPATH 时以模块方式调用 CLI:

PYTHONPATH=src python -m e_minus_minus.transpiler examples/describe.emm

在自己的软件中集成 E–

E– 基于 Apache 许可证 2.0 授权(参见 LICENSE)—— 宽松许可,包含明确的专利授权,因此可以自由地嵌入到商业产品中。

两点说明:

  • 许可证仅覆盖 E– 工具本身。 E– 生成的 Python 代码归您所有——输出不受本项目许可证的约束。
  • LLM 是您自己的。 E– 的规范化器和 {{ }} 解析需要您提供的语言模型;其提供商条款与本项目无关。

程序化 API:

``python from e_minus_minus import transpile

python_source = transpile(emm_source) ``

transpile() 是纯函数:无网络请求、无副作用。传递一个 resolve_slot 可调用对象来处理 {{ ... }} 槽(参见 docs/spec.mdsrc/transpiler.py 中的 CLI 实现,了解注入解析器的模式)。

工作原理

E– 是一个两阶段管道,不可靠部分与确定性部分从不混合:

自由英语 --LLM(转译时)--> 规范 E-- --普通解析器--> Python

  • 规范化器(LLM,可选)。 将自由格式的英语转换为规范 E–。这是唯一处理语言歧义的阶段。
  • 编译器(确定性)。 使用普通解析器将规范 E– 转换为 Python——无需 LLM、完全可复现和可调试。

LLM 仅在转译时运行,绝不运行。 生成的 Python 始终是纯的且自包含的。LLM 绝不允许决定程序结构;它只用于填充明确的界限分明的值槽(写作 {{ ... }}),并且这些解析结果会被缓存,以便构建保持可复现。

尝鲜

规范 E–:

Set result to [[fibonacci]]( {{the first prime number greater than 5}} ). Do [[print]](result).

编译为:

python result = fibonacci(7) print(result)

标记使其明确:[[name]] 是函数调用,裸词是变量,"x"/3 是字面量,<1, 2, 3> 是列表,{{ ... }} 是英语短语,转译器解析一次并嵌入其结果。

运行 E–

E– 源文件使用 .emm 扩展名(English–)。确定性规范到 Python 的核心已实现;您可以从命令行转译并运行 .emm 文件。

给定以下规范 E– 源文件 examples/describe.emm

`` Define [[describe]] taking n: If n is greater than 10: Give back “big”. Give back “small”.

For each n in <3, 42, 7>: Do [print]. ``

转译并在屏幕上打印生成的 Python:

python3 src/transpiler.py examples/describe.emm

输出:

python def describe(n): if n > 10: return "big" return "small" for n in [3, 42, 7]: print(describe(n))

将生成的 Python 写入文件而不是屏幕:

python3 src/transpiler.py examples/describe.emm -o out.py

转译并运行,以便看到程序的实际输出:

python3 src/transpiler.py examples/describe.emm --run

输出:

small big small

使用 --show(别名 -s)同时查看生成的 Python 代码并运行:

python3 src/transpiler.py examples/describe.emm --run --show

输出代码及其输出,以注释行分隔:

``

— generated Python —

def describe(n): if n > 10: return “big” return “small” for n in [3, 42, 7]: print(describe(n))

— output —

small big small ``

分隔符是 Python 注释,因此整个块保持可复制粘贴。单独使用 --show(不带 --run)仅打印 Python 代码,与默认行为相同。

注意:

  • .emm 扩展名是 E– 源文件的约定。
  • {{ ... }} LLM 值槽是可运行的——请参见下面的“解析 {{ }} 槽“了解一次性设置。没有槽的文件(如 examples/describe.emm)不需要密钥,--run 无需模型即可工作。

解析 {{ }} 槽(LLM 设置)

{{ ... }} 槽是英语短语,转译器在转译时使用 LLM 将其解析为 Python 表达式一次,然后缓存结果,以便后续构建离线且可复现。没有 {{ }} 槽的文件不需要 API 密钥,无需设置。

要端到端运行一个槽示例:

``

1. 创建并激活虚拟环境

python3 -m venv .venv && source .venv/bin/activate

2. 安装依赖(Anthropic SDK)

pip install -r requirements.txt

3. 设置您的 Anthropic API 密钥

export ANTHROPIC_API_KEY=“sk-ant-…”

4. 转译并运行一个槽示例

python3 src/transpiler.py examples/primes.emm –show –run ``

examples/primes.emm 是最简示例:

For each p in {{the first five prime numbers, as a Python list}}: Do [[print]](p).

转译为:

python for p in [2, 3, 5, 7, 11]: print(p)

并输出 2 3 5 7 11(每行一个)。{{ ... }} 槽通过 LLM 一次性解析为具体列表 [2, 3, 5, 7, 11],被缓存,并在后续每次构建中直接使用,无需 API 调用。

第一次运行调用模型(Anthropic Haiku)解析每个槽,将解析出的 Python 表达式写入 .emm_cache.json,并将其嵌入输出。之后的每次运行都是离线缓存命中——无模型调用,结果一致。缓存文件将确切的槽文本映射到其解析后的表达式,应提交到版本控制,以便解析后的值保持可对比和可审查。

编辑槽的文本会导致缓存未命中并重新解析;删除缓存会强制完全重新解析。没有 {{ }} 槽的文件(如 examples/describe.emm)永远不会触及 API。

一个单行值槽示例

您也可以在不进行任何文件设置的情况下内联构建一个槽示例:

printf 'Set year to {{the current year, as an integer literal}}.\nDo [[print]](year).\n' > hello.emm emm-transpile hello.emm --show --run

第 1 行的槽位于表达式位置(在 Set year to ... 内部),因此 LLM 返回一个单独的 Python 表达式——例如 2026——编译器将其嵌入:

python year = 2026 print(year)

输出 2026。第二次运行是离线的(缓存命中)。

代码槽(v0.2.0)

{{ ... }} 槽可以出现在语句位置,而不仅仅是表达式位置——将其放在单独一行,与块的缩进对齐,将一条或多条 Python 语句委托给 LLM。作者编写外围结构;LLM 填充该区域。

Define [[summarize]] taking numbers: {{ compute mean, median and count of numbers into mean_v median_v count_v }} Do [[print]](count_v). Do [[print]](mean_v). Give back mean_v.

在转译时,语句槽解析为真实的 Python:

python def summarize(numbers): from statistics import mean, median mean_v = mean(numbers) median_v = median(numbers) count_v = len(numbers) print(count_v) print(mean_v) return mean_v

权衡:代码槽解析后的 Python 内部的 [[wikilinks]] 或可调用引用对于任何检查 E– 源的下游工具是不透明的。作者明知接受在代码槽区域内 DAG 不可见性,以换取区域级别的委托。当需要图可见性时使用表达式槽;当委托 LLM 比您更熟悉的结构时使用代码槽。

可运行的代码槽示例

examples/code_slot_example.emm 是一个可运行的代码槽演示:

`` Define [[summarize]] taking numbers: {{ compute the mean, median and count of numbers into named variables mean_v median_v and count_v }} Do [print]. Do [print]. Do [print]. Give back mean_v.

Set data to <2, 3, 5, 7, 11, 13>. Do [summarize]. ``

转译并运行:

emm-transpile examples/code_slot_example.emm --show --run

第 2 行的语句槽解析为绑定 mean_vmedian_vcount_v 的真实 Python——例如通过 from statistics import mean, median 加上三个赋值。由于是代码槽,它可以在自己的行上添加 import,而值槽无法做到这一点。

代码槽让您委托导入。 位于表达式位置的值槽只能输出一个单独的 Python 表达式,因此通过值槽获取当前年份会产生笨拙的 __import__('datetime').datetime.now().year。语句位置的代码槽绕过了这一限制:

printf '{{ import datetime and set x to the current year }}\nDo [[print]](x).\n' > hello.emm emm-transpile hello.emm --show --run

LLM 将语句槽解析为:

python import datetime x = datetime.datetime.now().year print(x)

输出当前年份。语法与值槽相同;位置决定形状。

用自由英语编写

您不必手动编写规范 E–。转译器的第一阶段使用 LLM 规范化自由英语 E– 为规范 E–,然后将规范形式编译为 Python——一个输入,两个输出。一个英语源文件(examples/describe_en.en)读起来像散文:

Define a function called describe that takes a number n. If n is greater than ten, give back the string "big". Otherwise, give back the string "small". Then, for each n in the list 3, 42 and 7, print describe of n.

将其规范化为规范形式并运行结果,同时保存规范形式:

python3 src/transpiler.py examples/describe_en.en --canonical-out out.em --run

out.em 保存规范 E–(等同于 examples/describe.emm),程序输出 small / big / small

两个特性使其安全且廉价:

  • 解析器是规范检测器。 一个文件“是否已经是规范形式“通过尝试用确定性方式解析它来决定——无需 LLM,无需启发式。已经是规范形式的文件不需要 API 密钥:规范化在任何模型调用之前短路。只有真正的英语输入才会触及模型。
  • 不动点 + 缓存。 将规范输出(out.em)再次输入会解析为规范形式,因此阶段 1 什么都不做,输出相同的结果。规范化结果会缓存在一个已提交的 .emm_norm_cache.json 中(以源文本作为键),因此重新运行英语输入是离线的缓存命中。设置与槽相同:pip install -r requirements.txtexport ANTHROPIC_API_KEY=...

规范化和 {{ }} 槽解析是独立的、分别缓存的 LLM 接触点——所有槽都已缓存的规范文件实时调用。

状态

早期设计。语言规范在 docs/spec.md 中。确定性规范到 Python 核心(词法分析器、解析器、发射器)已实现,并带有可运行的 CLI——请参见上面的“运行 E–“——并且 {{ }} 槽解析已连接(Anthropic Haiku + 提交的缓存;请参见“解析 {{ }} 槽”)。LLM 规范化器(自由英语 → 规范)已实现为整个文件粒度(请参见“用自由英语编写“);逐区域规范化是下一步改进。

文档

相似文章

FEXPRs vs. vtable:LispE 解释器的工作原理

Lobsters Hottest

LispE 是 NAVER 开发的一个紧凑的 Lisp 方言,结合了函数式和数组语言特性,提供了针对 PyTorch、GGUF、MLX 和 tiktoken 的 AI 库,并附带一个基于浏览器的测试环境。

一种为人类设计的编程语言

Lobsters Hottest

Eat Your Greens(EYG)是一种静态类型函数式编程语言,旨在通过消除系统级关注点来简化终端用户编程,让创客能够专注于问题逻辑。

将 Python 转译为 Lisp

Lobsters Hottest

LispE 是 NAVER 推出的一款开源 Lisp 方言,兼具函数式与数组编程特性,并支持 PyTorch、llama.cpp 以及 MLX 等 AI 库。该语言既可作为原生应用运行,也可打包为支持多线程与现代函数式编程特性的 WebAssembly 库。

Show HN: Nibble

Hacker News Top

Nibble 是一种类 C 的系统编程语言,用 3000 行 C 代码实现,无需外部依赖或堆分配即可生成 LLVM IR。它支持 defer、递归、多种类型、结构体、指针,并包含图形演示。