在BQN中设计一个套盒结构
Scheming a mise-en-abîme in BQN

原始链接: https://panadestein.github.io/blog/posts/si.html#fnr.2

这段BQN代码定义了一个针对R5RS子集的简易Scheme解释器。它利用BQN的面向对象特性创建了一个名为`C`的环境类,用于管理Scheme的原语和变量。`env`变量实例化了这个类,并预先填充了核心函数,例如算术运算、列表操作和基本超越函数。 解释器的核心是`_sch` 1-修饰符,它充当求值器。它实现了读取-求值-打印 (REP) 循环(虽然缺少“L”循环),解析Scheme代码,在定义的环境中求值表达式,并打印结果。代码包括解析 (R)、环境查找 (E) 和原语执行。它处理基本的结构,例如`if`、`define`、`lambda`和函数应用。虽然功能强大,足以实现一个合理的子集,但它缺乏健壮的错误处理,这是其所基于的参考实现的一个已知限制。尽管它有相对简洁的BQN实现,但由于包含元编程功能,它比基本的Scheme实现要大。


原文

Our goal is to adhere to the Revised\(^5\) Report on the Algorithmic Language Scheme (R5RS). However, seasoned schemers will quickly notice that our implementation still has quite some distance to cover in reaching full compliance.

Let's start by defining some utilities. One aspect I don't like about Scheme is that it uses special values for Booleans, so we unfortunately need the 1-modifier. The function, on the other hand, is a fine example of the minimalistic OOP features BQN provides. It is used to create a class for the environment used in the Scheme interpreter.

_bool ← {𝔽◶"#f"‿"#t"}
C ← {𝕨𝕊p‿v:
  o‿h ⇐ 𝕨 ⋈ p •HashMap v
  F ⇐ {h.Has 𝕩 ? h; @≢o ? o.F 𝕩; 0}
}

We then define a global environment (instance of the C class) with the Scheme primitives of the target subset, expressed as BQN functions:

env ← @ C ⟨
  "sin", "cos", "tan", "asin", "acos", "atan"
  "log", "+", "-", "*", "/", ">", "<", ">=", "<=", "="
  "abs", "append", "apply", "begin", "car", "cdr", "cons"
  "eq?", "expt", "equal?", "length", "list", "list?"
  "map", "max", "min", "not", "null?", "number?"
  "print", "round", "symbol?", "nil", "pi"
⟩ ⋈ ⟨
  ⋆⁼, +´, -˜´⌽, ×´, ÷˜´⌽, >´, <´, ≥´, ≤´, =´
  |, ∾´, {𝕎𝕩}´, {∾𝕩}, ⊑∘∾, 1⊸↓∘∾, <⊸∾´
  ≡´_bool, ⋆´, =´_bool, ≠∘∾, ⊢, (0=•Type∘⊑)_bool
  {𝕎∘⋈¨𝕩}´, ⌈´, ⌊´, 0⊸≠_bool¬, @⊸=_bool, (1=•Type∘⊑)_bool 
  {𝕩}, ⌊0.5+⊢, 2⊸=_bool{•Type⊑∾𝕩}, @, π
⟩ ∾˜ •math •ns.Get¨ "sin"‿"cos"‿"tan"‿"asin"‿"acos"‿"atan"

The interpreter is defined as a 1-modifier. This gives us the flexibility to create different subsets of the language by changing the input global environment:

_sch ← {
  T ← " "⊸≢¨⊸/·(-⟜1·+`·¬⊸∧⟜»⊸∨·+˝"( )"=⌜⊢)⊸⊔(⊢+22×@=10-˜⊢)
  R ← {
    𝕊⟨⟩: "Empty program"!0;
    𝕊𝕩: {
      "("≡⊑𝕨 ? l←⟨⟩ ⋄ l⋈1↓{t‿ts: ts⊣l∾↩<t}∘R•_while_(")"≢⊑) 𝕩;
      ")"≡⊑𝕨 ? "Unexpected )"!0 ;
      𝕩 ⋈˜ •ParseFloat⎊⊢ ⊑𝕨
    }´ 1(↑⋈↓)𝕩
  }
  E ← 𝕗⊸{
    0≠𝕨.F 𝕩 ? (𝕨.F 𝕩).Get 𝕩;
    1=•Type⊑⟨𝕩⟩ ? 𝕩;
    𝕨𝕊"quote"‿arg: arg;
    𝕨𝕊"quasiquote"‿arg: 𝕨{"unquote"≡⊑𝕩 ? 𝕗𝔾1↓𝕩; (2≤≠)◶⊢‿(𝕊¨)𝕩}𝕊arg;
    𝕨𝕊"if"‿tst‿cnd‿alt: 𝕨(⊣𝕊𝕊◶alt‿cnd)tst;
    𝕨𝕊"define"‿var‿val: ⟨⟩ ⊣ var 𝕨.h.Set 𝕨𝕊val;
    𝕨𝕊"lambda"‿par‿bod: 𝕨{bod 𝕘˜ 𝕗 C par‿𝕩}𝕊;
    f ← 𝕨𝕊⊑𝕩 ⋄ F 𝕨⊸𝕊¨1↓𝕩 
  }∘⊑
  P ← "-(@  )" {'@'⊸≠⊸/·(⊢+˝(𝕗-𝕘)×𝕘=⌜⊢)∘•Repr·1⊸=∘≠◶⊢‿⊑(0<≠¨)⊸/⎊⊢} "¯⟨"",‿⟩"
  P∘E∘R∘T 𝕩
}

And now for the climax. Our interpreter inherits all the limitations of the one in the reference essay, the most critical being the lack of proper error handling. Additionally, as the names of the functions inside the modifier suggest, an L is missing to complete the Read → Eval → Print loop. In terms of golfing statistics, lispy has 117 non-comment non-blank lines, whereas Scheme has only 43. Ours, however, is a larger subset, because we include the basic metaprogramming building blocks.

联系我们 contact @ memedata.com