标准库:原生函数怎么接进你的语言

目标

上节用了 print/len/map,但没讲清"原生函数到底是怎么接进语言的"。本节课专门讲标准库(Standard Library):宿主提供的能力,如何通过环境注册,成为语言的一部分。下面给出完整可运行程序。

运行环境:Node.js v18+。运行方式:保存为 stdlib.js,执行 node stdlib.js(复用 data.js 解释器,重点看 createGlobalEnv)。

// stdlib.js —— 重点:标准库就是一批预先 define 进全局环境的原生函数
function createGlobalEnv() {
  const env = new Environment()
  // 原生函数:直接用 JS 函数,解释器在 call 节点里 typeof === 'function' 时直接调用
  env.define('print', (...xs) => console.log(...xs))
  env.define('len', x => (x && x.length !== undefined) ? x.length : 0)
  env.define('push', (a, x) => { a.push(x); return a })
  env.define('map', (a, fn) => a.map(e => fn(e)))
  env.define('max', (a, b) => (a > b ? a : b))
  env.define('PI', 3.14159) // 常量也能注册
  return env
}

function run(src) {
  const env = createGlobalEnv()
  for (const s of parse(src)) ev(s, env)
}

// ===== 调用示例:用户函数调用原生 max =====
run(`
func bigger(a, b) {
  return max(a, b);
}
print(bigger(3, 7));   // 7
print(PI);             // 3.14159
`)

标准库函数和用户函数的唯一区别:前者是 JS 函数(解释器直接执行),后者是 UserFn(解释器走 AST)。在 call 节点里用 typeof callee === 'function' 分流,二者对调用方完全透明。

名词解释

标准库(Standard Library):语言自带的、开箱即用的函数与常量集合(如 print/len/max/PI)。它把"宿主能力"包装成语言原生语法,用户无需关心底层实现。
宿主(Host):运行你这门语言的那个环境。这里宿主是 JS/Node;如果是浏览器里的语言,宿主就是浏览器(能调用 DOM)。
注册(register):把原生能力 define 进全局环境,使它在语言里"可见可用"。标准库就是一批预先 define 好的东西。

课后练习

练习:在标准库里加一个 input(prompt),当用户没提供交互时返回固定字符串 "guest"(避免依赖终端输入,便于教学演示)。

答案:

env.define('input', (prompt) => {
  // 真实环境可用 readline 同步读取;教学版返回占位值
  return 'guest'
})
// 调用:print(input("name:"))  // 输出 guest

总结

标准库是"语言"与"宿主能力"之间的桥。一门语言强不强,一半看语法、一半看标准库给了多少好用的原语。设计标准库时有两条路:要么像我们这样直接复用宿主函数(快、省事,但语言能力受宿主限制);要么自己用语言实现一套(慢、可控,但工作量巨大)。真实语言(如 Python)的标准库大多是"热点用 C 写、其余用 Python 写"。理解注册机制后你会明白:print 不是语言内建的魔法,它只是某个人在全局环境里 define 了一个函数而已——你也可以 define 任何自己想要的东西。