--- title: Monkey 基本语法 date: 2026-10-21 tags: monkey, 语法, 教程 summary: 十分钟过一遍值、变量、运算、条件、函数、数组、哈希、内置函数和宏 --- # Monkey 基本语法 交互式 REPL 按行解析:下面的多行代码块适合保存成 `.monkey` 脚本;在 REPL 里要写成一行,或者把多条语句用 `;` 串起来交给 `monkey -e`。仓库里的 [`demo.monkey`](/posts/monkey/demo.monkey) 就是一个可以直接运行的脚本。 ## 值 ```monkey 42 "hello" true false [1, 2, 3] {"name": "monkey", "year": 2026} ``` 数组用下标取值,哈希用键取值: ```monkey [10, 20, 30][1] {"name": "monkey"}["name"] ``` `type_of` 可以看类型:`INT`、`STRING`、`BOOL`、`ARRAY`、`HASH`、`FUNCTION`、`NULL`。 ## 变量 ```monkey let x = 10; let name = "monkey"; x + 3 ``` 标识符只能由字母和下划线组成(`addOne` 可以,`add1` 不行)。变量用 `let` 绑定,没有 `var` / `const`。 ## 运算 ```monkey 1 + 2 // 3 10 - 4 // 6 3 * 7 // 21 5 / 2 // 2,整数除法 1 < 2 // true 1 == 1 // true 1 != 2 // true !true // false ``` 只有 `==`、`!=`、`<`、`>`;没有 `<=`、`>=`、`&&`、`||`。需要组合条件时用嵌套 `if`。 ## 条件 `if` 是表达式,会返回值: ```monkey let max = fn(a, b) { if (a > b) { a } else { b } }; max(3, 8) ``` 没有 `else` 时返回 `null`。`if` 的结果就是被选中的分支里最后一个表达式的值。 ## 函数 ```monkey let square = fn(x) { x * x }; square(7) ``` 函数是值,可以返回函数,形成闭包: ```monkey let makeAdder = fn(n) { fn(x) { x + n } }; let addTen = makeAdder(10); addTen(5) ``` 递归直接引用函数名: ```monkey let factorial = fn(n) { if (n == 0) { 1 } else { n * factorial(n - 1) } }; factorial(5) ``` 也可以显式 `return`: ```monkey let add = fn(a, b) { return a + b; 999 }; add(2, 3) ``` ## 数组与内置函数 ```monkey len("hello") // 5 len([1, 2, 3]) // 3 first([1, 2, 3]) // 1 last([1, 2, 3]) // 3 rest([1, 2, 3]) // [2, 3] push([1, 2], 3) // [1, 2, 3] ``` 这个解释器还预置了几个用 Monkey 自己写的函数: ```monkey map([1, 2, 3, 4], fn(x) { x * x }) reduce([1, 2, 3, 4], 0, fn(acc, x) { acc + x }) sum([1, 2, 3, 4]) fibonacci(10) ``` - `map`:对数组每个元素调用函数,返回新数组。 - `reduce`:从初始值开始,把数组折叠成一个值。 - `sum`:`reduce` 的简写。 - `fibonacci`:递归版斐波那契。 ## 输出 `puts` 把参数打印出来,每个参数一行,返回 `null`: ```monkey puts("factorial(5) =", factorial(5)) ``` ```text factorial(5) = 120 null ``` 最后那个 `null` 是 `puts` 的返回值,REPL 会把它也打印出来。 ## 注释 ```monkey // 这一行会被忽略 1 + 1 ``` ## 宏 宏在求值前展开,可以做语法层面的封装: ```monkey let unless = macro(cond, cons, alt) { quote(if (!(unquote(cond))) { unquote(cons); } else { unquote(alt); }); }; unless(1 > 2, "no", "yes") ``` 这个解释器已经预置了一个 `unless`,上面的定义只是演示宏的写法;重新定义会覆盖它。 ## 一个完整例子 ```monkey let square = fn(x) { x * x }; let factorial = fn(n) { if (n == 0) { 1 } else { n * factorial(n - 1) } }; puts(factorial(5)); puts(map([1, 2, 3, 4, 5], square)); puts(fibonacci(10)); sum([1, 2, 3, 4, 5]); ``` 保存成文件后运行: ```console visitor@localhost:~$ monkey posts/monkey/demo.monkey ```