Hook 编程语言

精选 Hook 常用指令与核心速查备忘单,涵盖高频用法、配置参数与实用技巧。 Hook 编程语言的单页核心语法速查备忘单。

#🚀 入门指引

#Hook 代码风格概览

fn factorial(n) {
  if (n == 0)
    return 1;
  return n * factorial(n - 1);
}

Hook 拥有类似 C 语言风格的现代化简洁语法。

#Hello, world! 示例

println('Hello, World!');
// Hello, World!

Hook 中的 Hello, World! 入门程序。

#通过 Homebrew 安装

brew tap hook-lang/hook
brew install hook
hook --help

Hook 解释器可以通过 Homebrew 快速安装。

#在 Windows 上安装

cd %tmp%
curl -sSLO https://raw.githubusercontent.com/hook-lang/hook/main/scripts/install.bat
install

这是在 Windows 系统下的安装步骤。

#数据类型与变量值 (Types and Values)

#基本数据类型 (Basic Types)

类型标识 类型标识
Nil Bool
Number String
Range Array
Record Closure

基本数据类型列表。

#布尔类型 (Bool)

let x = true;
let y = false;

Bool 是布尔类型,其值可以为 truefalse

#数字类型 (Numbers)

let x = 0;
let degree = 45; // 整数 (integer number)
let pi = 3.14; // 浮点数 (floating-point number)

数字类型可以是整数,也可以是浮点数。

#字符串类型 (Strings)

let empty = '';

let name = 'John';

let message = 'Hello, "John"!';

字符串可以使用单引号或双引号包裹。

#范围类型 (Ranges)

let range = 1..5;

println(range);
// 1..5

Range 表示一个连续的整数序列。

#数组类型

let fruits = ['apple', 'banana', 'cherry'];

println(fruits);
// ["apple", "banana", "cherry"]

Array 是元素的有序序列。

#记录类型

let p = { x: 5, y: 10 };

println(p);
// {x: 5, y: 10}

Record 将属性映射至对应的具体数值。

#nil 空值 (The nil value)

let x = nil;
var y;
println(x); // nil
println(y); // nil

nil 表示变量无值或空值。

#假值条件 (Falsy values)

if (nil) "true" else "false";   // false
if (false) "true" else "false"; // false
if (true) "true" else "false";  // true
if (0) "true" else "false";     // true
if (1) "true" else "false";     // true
if ("") "true" else "false";    // true
if ([]) "true" else "false";    // true
if ({}) "true" else "false";    // true

在 Hook 中只有 nilfalse 会被评估为假值。

#基本语法 (Syntax)

#代码注释

// 这是一个单行注释。

// 这也是
// 多行联排
// 单行注释。;)

Hook 目前仅支持单行注释。

#分号规范 (Semi-colons)

println(1) ; println(2) ; println(3) ;
println(4) ; println(5)
; println(6) ;
;                                      // 报错 error: unexpected token `;`

分号 ; 是必填的,且不允许出现空语句。

#代码块 (Blocks)

{
  println('Hello');
  {
    println('World');
  }
}

代码块用于定义作用域。

#保留关键字 (Reserved words)

关键字 关键字 关键字 关键字
as break continue do
else false fn for
from if import in
inout let loop match
nil return struct trait
true var while

Hook 的保留关键字非常精简。

#标识符命名 (Identifiers)

var lowercase;
var CAPS_LOCK;
var camelCase;
var PascalCase;
var snake_case;
var _123;

标识符严格区分大小写。

#变量声明 (Variables)

#动态变量 (Variables)

var x; // x 包含 nil
x = 5; // 现在 x 包含数字
x = 'foo'; // 赋值为字符串

println(x);

数据值拥有类型,但变量本身没有类型约束。

#不可变变量 (Immutable variables)

let x = 5;

x = 10; // 报错 error: cannot assign to immutable variable `x`

let y; // 报错 error: unexpected token `;`

不可变变量在声明时必须显式初始化。

#作用域范围 (Scopes)

let x = 5;
{
  let y = 15;
  println(x); // 10
  println(y); // 15
}
println(x); // 5
println(y); // 报错 error: variable `y` is used but not defined

当堆分配变量超出其作用域时,会被自动释放。

#变量遮蔽 (Shadowing)

let x = 5;
{
  let x = 10; // 遮蔽外层 `x`
  println(x); // 10
}
println(x); // 5

局部作用域内的变量可以对同名外层变量进行遮蔽。

#运算符与表达式

#算术运算符 (Arithmetic)

println(5 + 10); // 15
println(5 - 10); // -5
println(5 * 10); // 50
println(5 / 10); // 0.5
println(5 % 10); // 5
println(-5); // -5

基础算术运算符。

#比较运算符 (Comparison)

println(5 == 10); // false
println(5 != 10); // true
println(5 < 10); // true
println(5 > 10); // false
println(5 <= 10); // true
println(5 >= 10); // false

基础比较运算符。

#逻辑运算符 (Logical)

println(true && false); // false
println(true || false); // true
println(!true); // false

基础逻辑运算符。

#位运算与移位 (Bitwise and shift)

println(5 & 10); // 0
println(5 | 10); // 15
println(5 ^ 10); // 15
println(~5); // -6
println(5 << 1); // 10
println(5 >> 1); // 2

位运算符与移位运算符。

#赋值运算符 (Assignments)

var x = 5; // 5
x += 10; // 15
x -= 10; // 5
x *= 10; // 50
x /= 10; // 5
x %= 10; // 5
x &= 10; // 0
x |= 10; // 10
x ^= 5; // 15
x <<= 5; // 480
x >>= 5; // 15
x++; // 16
x--; // 15

各种复合赋值运算符。

#三元条件运算符 (The ternary operator)

let x = 5;
let y = if (x > 5) 10 else 20;

println(y);
// 20

在 Hook 中,三元运算符采用 if else 表达式。

#条件分支 (Branching)

#If 条件语句

let x = 10;

if (x > 5) {
  println('x is greater than 5');
}
// x is greater than 5

if 条件分支语句。

#If else 分支语句 (If else)

let x = 11;

if (x == 5) {
  println('x is 5');
} else if (x == 10) {
  println('x is 10');
} else {
  println('x is neither 5 nor 10');
}
// x is neither 5 nor 10

if else 多重分支语句。

#Match 模式匹配 (Match)

let x = 5;

match (x) {
  1 => println("one");
  2 => println("two");
  3 => println("three");
  _ => println("other");
}
// other

match 模式匹配语句。

#循环结构 (Looping)

#While 循环

var x = 0;

while (x < 5) {
  print(x);
  x += 1;
}
// 01234

while 循环语句。

#Do while 循环 (Do while)

var x = 0;

do {
  print(x);
  x += 1;
} while (x < 5);
// 01234

do while 循环语句。

#For 循环

for (var i = 0; i < 5; i++) {
  print(i);
}
// 01234

标准的经典 for 循环语句。

#Loop 无条件循环 (Loop)

loop {
  println("Press Ctrl+C to stop");
}

无条件死循环 loop 语句。

#Break 跳出循环 (Break)

var i = 0;
loop {
  if (i == 5) break;

  print(i);
  i += 1;
}
// 01234

使用 break 提前跳出并结束循环。

#Continue 继续下次循环 (Continue)

var i = 0;
loop {
  i += 1;
  if (i % 2 == 0) continue;

  print(i);

if (i == 5) break;
}
// 135

使用 continue 跳过当前循环体剩余代码。

#字符串操作 (Strings)

#字符串索引 (Indexing a string)

let s = 'Hello';

println(s[0]); // H
println(s[1]); // e
println(s[4]); // o

对字符串进行索引访问将返回长度为 1 的字符子串。

#字符串切片 (Slicing a string)

let s = "Hello, World!";

println(s[0..5]);        // Hello,
println(s[7..12]);       // World!

传入范围 Range 即可对字符串进行切片。

#字符串拼接 (Concatening strings)

let greeting = 'Hi' + ' there!';

println(greeting);
// Hi there!

使用 + 运算符拼接多个字符串。

#数组操作 (Arrays)

#数组索引访问 (Indexing an array)

let a = [1, 2, 3];

println(a[0]); // 1
println(a[1]); // 2
println(a[2]); // 3

对数组进行索引访问将返回对应的单个元素。

#数组切片 (Slicing an array)

let a = [1, 2, 3, 4];

println(a[0..2]);            // [1, 2, 3]
println(a[1..3]);            // [2, 3, 4]
println(a[2 .. len(a) - 1]); // [3, 4]

数组索引从 0 开始。

#追加数组元素 (Appending an element)

var a = [1, 2];

a[] = 3;

println(a);
// [1, 2, 3]

数组是可变的,使用 [] 在末尾追加元素。

#修改数组元素 (Element assignment)

var a = [1, 2, 3];

a[0] = 4;

println(a);
// [4, 2, 3]

更新数组中指定索引处的元素。

#数组拼接 (Concatening arrays)

let a = [1, 2];
let b = [3];
let c = a + b;

println(c);
// [1, 2, 3]

使用 + 运算符合并拼接数组。

#数组减法 (Subtracting arrays)

let a = [1, 2, 2, 3];
let b = [2];
let c = a - b;

println(c);
// [1, 3]

计算获取两数组间的差集。

#函数与闭包 (Functions and Closures)

#函数声明 (Function declaration)

fn sum(a, b) {
  return a + b;
}

println(sum(5, 10));
// 15

在 Hook 中函数是一等公民 (First-class citizens)。

#函数调用 (Function call)

fn greet(name) {
  println("Hi, " + name + "!");
}

greet("John", "Doe");
// Hi, John!

多余的实参会被自动忽略调整。

#匿名函数 (Anonymous functions)

let sum = |a, b| {
  return a + b;
};

println(sum(5, 10));
// 15

同样支持使用 |a, b| 语法的匿名函数。

#闭包 (Closures)

let pi = 3.14;
fn area(r) {
  return pi * r * r;
}

println(area(5));
// 78.5

Hook 中的闭包仅按值捕获外部变量。

#高阶函数 (Higher-order functions)

fn apply(f, x) {
  return f(x);
}

fn double(x) {
  return x * 2;
}

println(apply(double, 5));
// 10

函数可以作为参数传递,也可以作为返回值返回。

#函数简写语法糖 (Syntax sugar for functions)

fn factorial(n) =>
  if (n == 0) 1
  else n * factorial(n - 1);

println(factorial(5));
// 120

当函数体为单个表达式时可以使用 => 简写。

#递归调用 (Recursion)

fn fib(n) {
  if (n < 2)
    return n;
  return fib(n - 1) + fib(n - 2);
}

println(fib(10));
// 55

原生支持函数的递归调用。

#内置函数 (Built-in functions)

println(type(5));
// number
println('1' + to_string(2));
// 12
println(len('foo'));
// 3

Hook 提供了丰富的内置原生函数。

#常用内置函数列表 (More built-in functions)

函数名 函数名 函数名
print println type
is_nil is_bool to_number
to_string hex len
exit assert panic

详情请参阅:Built-in Functions 内置函数文档

#结构体 (Structs)

#结构体定义 (Structs)

struct Point {
  x, y
}
let p = Point { 5, 10 };

println(p);
// {x: 5, y: 10}

结构体 (Struct) 是记录 (Record) 的原型模版。

#访问结构体属性 (Accessing fields)

println(p.x); // 5
println(p.y); // 10

使用点号 . 访问 Record 中的字段属性。

#属性赋值 (Field assignment)

p.x = 10;
p.y = 20;

println(p);
// {x: 10, y: 20}

更新修改 Record 中字段的值。

#解构赋值 (Destructuring)

#解构数组 (Destructuring an array)

let a = [1, 2];
let [x, y] = a;

println(x); // 1
println(y); // 2

直接对数组元素进行解构并声明变量。

#解构记录 (Destructuring a record)

let p = { x: 5, y: 10 };
let { x } = p;

println(x);
// 5

使用 {} 解构 Record 字段。

#占位符跳过 (Placeholder)

let a = [1, 2];
let [x] = a;
let [_, y] = a;

println(x); // 1
println(y); // 2

使用 _ 下划线跳过开头的元素。

#模块化系统 (Modularity)

#导入模块 (Importing a module)

import math;
println(math.sqrt(25));
// 5

使用 import 将模块引入当前作用域。

#导出符号 (Exporting symbols)

// my_module.hk
fn useful_fn() {
  return "Nothing";
}

return { useful: useful_fn };

返回一个包含要导出符号的 Record 记录。

#导入本地模块 (Importing local modules)

import "./my_module.hk" as my;
println(my.useful());
// Nothing

指定本地模块文件的相对路径。

#按需选择导入 (Selective import)

import { pow, sqrt } from math;

let [ b, c ] = [ 4, 3 ];
let a = sqrt(pow(b, 2) + pow(c, 2));

println(a);
// 5

使用 {} 挑选导入指定的模块符号。

#核心原生模块 (Core modules)

模块名 模块名 模块名 模块名
math os io numbers
strings arrays utf8 hashing
encoding socket json lists

详情请参阅:Core Modules 核心模块文档

#拓展模块 (Extension modules)

模块名 模块名 模块名 模块名
bigint crypto curl fastcgi
geohash leveldb mysql redis
regex sqlite uuid zeromq

扩展模块列表。

#io 标准输入输出模块 (io module)

import { stderr, writeln } from io;

writeln(stderr, "Something went wrong");
// Something went wrong

使用 io 模块向 stderr 标准错误流输出。

#hashing 哈希模块 (hashing module)

import hashing as h;
let d = h.sha256('Hello, world!');

println(hex(d));
// 315f5bdb76d078c43b8ac0064e4a...

hashing 模块提供常用的加密哈希函数。

#json 模块 (json module)

import json;
let j = '{"x": 1, "y": 2}';
let p = json.decode(j);

println(p.x); // 1

let k = json.encode(p);
println(type(k)); // string

使用 json 模块解析和生成 JSON 文本。

#异常与错误处理 (Error Handling)

#运行时错误 (Errors)

println(to_int('foo'));

// 运行时错误: runtime error: type error: argument #1 is not a convertible string
//   at to_int() in <native>
//   at main() in example.hk:1

Hook 使用 Panic 恐慌模式处理错误,发生错误时解释器会立即终止。

#语法错误 (Syntax error)

println('Hello, World!');

// 语法错误: syntax error: unexpected end of file
//   at main() in example.hk:1,25

Hook 拥有严格的语法解析逻辑。

#主动触发 Panic (Panic)

panic('Something went wrong');

// panic: Something went wrong
//   at main() in example.hk:1

使用内置函数 panic 抛出并报告异常错误。

#断言 (Assert)

assert(5 > 10, '5 is not greater than 10');

// assert: 5 is not greater than 10
//   at main() in example.hk:1

使用内置函数 assert 断言检查条件。

#返回错误结果 (Returning errors)

fn divide(a, b) {
  if (b == 0)
    return [nil, "division by zero"];
  return a / b;
}

if (let [ok, err] = divide(5, 0); ok) {
  println(ok);
} else {
  println(err);
}
// division by zero

使用元组对同时返回计算结果与错误消息。

#传递向上抛出错误 (Passing errors)

if (let [ok, err] = divide(5, 0); err) {
  return [nil, err];
}

直接向上传递错误而不进行处理。