| name | cangjie-lang |
| description | Cangjie programming language code generation and analysis expert. Use this when users need to write or analyze Cangjie code. |
仓颉语言代码生成与分析工作手册
目录
第1章:语言基础定义
1.1 文件与后缀
- 文件后缀:
.cj - 语言简称:
cj(Cangjie) - 入口函数:
main()
1.2 注释
// 单行注释
/*
* 多行注释
*/
/**
* 文档注释
*/
1.3 代码块结构
main() {
// 代码主体
let a: Int64 = 20
var b: Int64 = 12
println("${a}${b}")
}
第2章:核心语法规则
2.1 变量声明
语法格式
修饰符 变量名: 变量类型 = 初始值
可变性修饰符
let- 不可变变量(只能赋值一次,初始化后不可修改)var- 可变变量(可以被多次赋值)
⚠️ 易错点1:let 不支持变量遮蔽(shadowing),不能在同一作用域内重新定义同名变量。
可见性修饰符
| 修饰符 | 范围 | 默认值 |
|---|---|---|
private |
class定义内可见 | ❌ |
public |
模块内外均可见 | ❌ |
protected |
当前模块及当前类的子类可见 | ❌ |
internal |
仅当前包及子包内可见 | ✅ 默认 |
静态性修饰符
static- 影响成员变量的存储和引用方式
示例代码
main() {
let a: Int64 = 20 // 不可变变量
var b: Int64 = 12 // 可变变量
b = 23 // 可以修改var变量
println("${a}${b}")
}
示例参考:examples/01_variable_declaration.cj
2.2 基础类型
数值类型
有符号整数:Int8、Int16、Int32、Int64、IntNative
无符号整数:UInt8、UInt16、UInt32、UInt64、UIntNative
浮点类型:Float16、Float32、Float64
布尔类型:true、false
字符类型(Rune)
- 使用
Rune表示,可以表示 Unicode 字符集中的所有字符 - Rune → UInt32:
UInt32(e)- 获取 Unicode scalar value - 整数 → Rune:
Rune(num)- 值必须在有效 Unicode 范围内- 有效范围:
[0x0000, 0xD7FF]或[0xE000, 0x10FFFF]
- 有效范围:
⚠️ 易错点2:Rune 转整数时需确保在有效 Unicode 范围内,否则会编译错误或运行时抛异常。
转义字符
let slash: Rune = r'\\'
let newLine: Rune = r'\n'
let tab: Rune = r'\t'
Unicode 字面量
let he: Rune = r'\u{4f60}' // 你
let llo: Rune = r'\u{597d}' // 好
字符串类型(String)
仓颉支持三种字符串字面量:
1. 单行字符串字面量
内容定义在一对单引号或双引号内,只能写在同一行:
let s1: String = ""
let s2 = 'Hello Cangjie Lang'
let s3 = "\"Hello Cangjie Lang\""
let s4 = 'Hello Cangjie Lang\n' // \n 是转义字符,表示换行
2. 多行字符串字面量
开头结尾需各存在三个双引号(""")或三个单引号('''),内容从开头的三个引号换行后的第一行开始:
// 三个双引号
let s1: String = """
""" // 空字符串(换行后才开始)
// 三个单引号
let s2: String = '''
Hello,
Cangjie Lang'''
3. 多行原始字符串字面量
以一个或多个井号(#)和一个单引号或双引号开头,转义规则不适用,不支持插值:
let s1: String = #""# // 空字符串
let s2 = ##'#'\n'## // \n 不是换行符,是 \ 和 n 两个字符
let s3 = ###"
Hello,
Cangjie
Lang"### // 保留换行和缩进
⚠️ 易错点4:在多行原始字符串字面量中,转义字符(如 \n、\t)不会被转义,字面量中的内容会维持原样。
⚠️ 易错点5:多行原始字符串字面量不支持插值,不要使用 ${} 语法。
插值字符串
⚠️ 易错点3:插值字符串中使用 ${} 表达式
let fruit = "apples"
let count = 10
let s = "There are ${count * count} ${fruit}"
适用范围:插值字符串仅适用于单行字符串字面量和多行字符串字面量,不适用于多行原始字符串字面量。
元组(Tuple)
- 类型表示:
(T1, T2, ..., TN) - 至少二元:
(Int64, Float64)、(Int64, Float64, String) - 索引访问:
tuple[0]、tuple[1]
var tuple = (true, false)
println(tuple[0])
数组类型
Array<T> // T 是元素类型,可以是任意类型
区间类型(Range)
- 类型表示:
Range<T>(泛型) - 包含三个值:
start、end、step - 约束:
start和end类型相同(T)step类型是Int64step值不能等于 0
Unit 类型
- 唯一值:
() - 支持操作:仅赋值、判等、判不等
- ⚠️ 不支持:其他所有操作
示例参考:examples/02_basic_types.cj
2.3 表达式与控制流
条件表达式
⚠️ 易错点5:条件表达式的括号不能省略,这是与很多语言的差异。
if (条件) { // ✅ 必须有括号
分支 1
} else {
分支 2
}
if 表达式
基本形式:
if (条件) {
分支 1
} else {
分支 2
}
if 作为表达式(返回值):
let a = 10
let result = if (a > 5) {
"greater than 5"
} else {
"5 or less"
}
// result = "greater than 5"
模式匹配(let pattern):
let a = Some(3)
let d = Some(1)
if (let Some(e) <- a && let Some(f) <- d) { // 两个模式都匹配
println("${e} ${f}") // 输出: 3 1
}
while 表达式
while (条件) {
循环体
}
// do-while 形式
do {
循环体
} while (条件)
for-in 表达式
for (迭代变量 in 序列) {
循环体
}
元组遍历:
let array = [(1, 2), (3, 4), (5, 6)]
for ((x, y) in array) {
println("${x}, ${y}")
}
区间遍历:
main() {
var sum = 0
for (i in 1..=100) { // 1 到 100(包含)
sum += i
}
println(sum)
}
跳转控制
- 支持
break、continue - ⚠️ 易错点6:不支持标签跳转
- ⚠️ 易错点7:完全不支持
goto
示例参考:examples/03_control_flow.cj
2.4 函数
函数是一等公民
- 可以作为函数的参数或返回值
- 可以赋值给变量
- 函数本身也有类型
函数类型
语法:(参数类型) -> 返回类型
- 参数类型用
()括起,多个参数用,分隔 - 参数类型和返回类型用
->连接
func add(a: Int64, b: Int64): Int64 {
return a + b
}
type FnType = (Int64) -> Unit
func display(a: Int64): Unit {
println(a)
}
// 命名参数
func name(name!:String)
// 命名参数还可以设置默认值
func name(name!:String = "小王")
函数参数
⚠️ 易错点8:函数参数默认是 let 定义的不可变变量
// a 和 b 默认是 let 不可变的(不能显式写 let 或 var)
func add(a: Int64, b: Int64): Int64 {
return a + b
}
⚠️ 重要:
- 函数参数不能显式使用
let或var修饰符(包括顶层函数、class成员方法、struct成员方法等所有函数) - 参数默认就是
let不可变的
如果需要修改参数值,应该使用局部变量:
func modify(a: Int64): Int64 {
var result = a
result = result + 1 // 修改局部变量
return result
}
返回值简写
func add(a: Int64, b: Int64): Int64 {
a + b // 最后一个表达式自动作为返回值
}
func returnAdd(): (Int64, Int64) -> Int64 {
add // 可以直接返回函数
}
Lambda 表达式
语法:
{ p1: T1, ..., pn: Tn => expressions | declarations }
示例:
// 完整类型声明
let f1 = { a: Int64, b: Int64 => a + b }
// 无参 Lambda
var display = { =>
println("Hello")
println("World")
}
// 类型推断
var sum1: (Int64, Int64) -> Int64 = { a, b => a + b }
var sum2: (Int64, Int64) -> Int64 = { a: Int64, b => a + b }
// Lambda 作为参数
func f(a1: (Int64) -> Int64): Int64 {
a1(1)
}
main(): Int64 {
f({ a2 => a2 + 10 }) // 参数类型推断
}
Lambda 立即调用:
⚠️ 易错点9:Lambda 表达式可以立即调用
let r2 = { => 123 }() // r2 = 123,立即执行
var g = { x: Int64 => println("x = ${x}") }
g(2) // 调用 Lambda
示例参考:examples/04_functions.cj
2.5 枚举类型
定义语法
- 以
enum开头 - 构造器之间使用
|分隔 - 至少存在一个有名字的构造器
- 第一个构造器之前的
|是可选的
enum RGBColor {
| Red(UInt8) | Green(UInt8) | Blue(UInt8)
}
构造器类型
- 无参构造器:
C - 有参构造器:
C(p1, p2, ..., pn)
示例参考:examples/05_enum.cj
2.6 模式匹配
match 表达式
基本语法:
⚠️ 重要:case 后的语句不需要 {} 括号,多行语句直接换行即可
match (待匹配值) {
case 模式1 => 处理1
case 模式2 => 处理2
case _ => 默认处理 // 通配符
}
多行语句:直接换行,不需要 {}
match (value) {
case 1 => println("one")
case 2 => println("two")
println("second number") // ✅ 多行直接换行
case _ => println("other")
}
示例:
main() {
let x = 0
match (x) {
case 1 => print("x = 1")
case 0 => print("x = 0") // 匹配
case 2 | 3 | 4 => print("other") // 多值匹配
case _ => print("其他")
}
}
模式类型详解
1. 常量模式
支持:整数字面量、浮点数字面量、字符字面量、布尔字面量、字符串字面量、Unit 字面量
⚠️ 易错点10:不支持字符串插值
2. 通配符模式
使用 _ 表示,匹配任意值,通常作为最后一个 case
3. 绑定模式
使用标识符,匹配并绑定值
main() {
let x = -10
let y = match (x) {
case 0 => "zero"
case n => "x is not zero and x = ${n}" // n 绑定匹配的值
}
println(y)
}
4. Tuple 模式
用于匹配元组值
main() {
let tv = ("Alice", 24)
let s = match (tv) {
case ("Bob", age) => "Bob is ${age} years old"
case ("Alice", age) => "Alice is ${age} years old" // age 是绑定模式
case (name, 100) => "${name} is 100 years old"
case (_, _) => "someone"
}
println(s)
}
5. 类型模式
判断运行时类型是否是某个类型的子类型
main() {
var d = Derived()
var r = match (d) {
case b: Base => b // b 是类型模式,匹配 Base 类型
case _ => 0
}
println("r = ${r}")
}
6. enum 模式
用于匹配 enum 类型的实例
enum TimeUnit {
| Year(UInt64)
| Month(UInt64)
}
main() {
let x = Year(2)
let s = match (x) {
case Year(n) => "x has ${n * 12} months" // 匹配
case TimeUnit.Month(n) => "x has ${n} months" // TimeUnit.Month 是完整路径
}
println(s)
}
模式嵌套
⚠️ 重要:Tuple 模式和 enum 模式可以嵌套任意模式
enum TimeUnit {
| Year(UInt64)
| Month(UInt64)
}
enum Command {
| SetTimeUnit(TimeUnit)
| GetTimeUnit
| Quit
}
main() {
let command = (SetTimeUnit(Year(2022)), SetTimeUnit(Year(2024)))
match (command) {
case (SetTimeUnit(Year(year)), _) => println("Set year ${year}")
case (_, SetTimeUnit(Month(month))) => println("Set month ${month}")
case _ => ()
}
}
Pattern Guard(守卫)
在 case 的模式之后,可以使用 where 子句添加额外的条件判断(称为 pattern guard)。
语法:case 模式 where 条件 => 处理
⚠️ 重要:使用 where 关键字,不是 if
示例:
enum RGBColor {
| Red(Int16)
| Green(Int16)
| Blue(Int16)
}
main() {
let c = RGBColor.Green(-100)
let cs = match (c) {
case Red(r) where r < 0 => "Red = 0"
case Red(r) => "Red = ${r}"
case Green(g) where g < 0 => "Green = 0" // 匹配(因为 -100 < 0)
case Green(g) => "Green = ${g}"
case Blue(b) where b < 0 => "Blue = 0"
case Blue(b) => "Blue = ${b}"
}
println(cs) // 输出:Green = 0
}
注意事项:
where后的条件表达式必须是Bool类型- pattern guard 在模式匹配成功后再进行条件判断
- 多个 case 可以按顺序排列,从上到下匹配
- ⚠️ 易错点:不要使用
if,应该使用where
错误示例:
match (value) {
case n if n > 0 => println("positive") // ❌ 编译错误:不是 if
case _ => println("other")
}
正确写法:
match (value) {
case n where n > 0 => println("positive") // ✅ 使用 where
case _ => println("other")
}
示例参考:examples/06_pattern_matching.cj
2.7 Option类型
定义
enum Option<T> {
| Some(T) // 有值
| None // 无值
}
使用场景
当需要表示某个类型可能有值,也可能没有值的时候使用。
常用方法
1. 空合并运算符 ??
let optA: Option<Int64> = Option<Int64>.Some(42)
let a: Int64 = optA ?? 0 // 如果 optA 是 None,则使用默认值 0
2. 检查是否有值
let optA: Option<Int64> = Option<Int64>.Some(42)
if (optA.isSome()) {
println("有值")
}
if (optA.isNone()) {
println("无值")
}
3. 模式匹配提取值
let optA: Option<Int64> = Option<Int64>.Some(42)
if (let Option<Int64>.Some(a) <- optA) {
println("值是: ${a}")
} else {
println("无值")
}
4. 短路返回
func getValue(): Option<Int64> {
// ... 一些逻辑
return Option<Int64>.None
}
func process(): Int64 {
let optA = getValue()
let a = optA ?? return 0 // 如果是 None,直接返回 0
// 继续处理 a
a + 1
}
5. 短路抛出异常
func getValue(): Option<Int64> {
return Option<Int64>.None
}
func processOrThrow(): Int64 {
let optA = getValue()
let a = optA ?? throw Exception("值不存在")
// 继续处理 a
a
}
6. match 表达式处理
let optA: Option<Int64> = Option<Int64>.Some(42)
let result = match (optA) {
case Option<Int64>.Some(value) => "值是: ${value}"
case Option<Int64>.None => "无值"
}
示例参考:examples/07_option_type.cj
2.8 class类型
定义语法
class ClassName {
// 成员变量
// 成员属性
// 静态初始化器
// 构造函数
// 成员函数
// 操作符函数
}
示例
class Rectangle {
let width: Int64
let height: Int64
public init(width: Int64, height: Int64) {
this.width = width
this.height = height
}
public func area() {
width * height
}
}
let rec = Rectangle(10, 20)
let l = rec.height // l = 20
访问修饰符
对于 class 的成员(变量、属性、构造函数、函数):
| 修饰符 | 含义 | 默认 |
|---|---|---|
private |
class 定义内可见 | ❌ |
internal |
当前包及子包(含子包的子包)内可见 | ✅ 默认 |
protected |
当前模块及当前类的子类可见 | ❌ |
public |
模块内外均可见 | ❌ |
⚠️ 易错点11:成员的默认访问修饰符是 internal,不是 private 或 public。
示例参考:examples/08_class.cj
2.9 单元测试
测试宏
@Test- 应用于顶级函数或类,转换为单元测试类@TestCase- 标记测试类内的函数为测试用例@Fail- 标记测试失败
断言宏
Assert 断言(失败停止用例)
@Assert(leftExpr, rightExpr) // 判断相等
@Assert(condition: Bool) // 判断条件
Expect 断言(失败继续执行)
@Expect(leftExpr, rightExpr) // 判断相等
@Expect(condition: Bool) // 判断条件
完整示例
@Test
class LexerTest {
@TestCase
func test() {
let a = 1
// 方式一:手动判断
if (a != 1) {
@Fail("a is not 1")
}
// 方式二:Assert 条件
@Assert(a != 1)
// 方式三:Assert 相等
@Assert(a, 1)
}
}
示例参考:examples/09_unit_test.cj
2.10 错误处理
基础异常处理
// 抛出异常
throw Exception("错误信息")
// 捕获异常
try {
// 可能抛出异常的代码
} catch (e: ExceptionType) {
// 处理异常
}
示例参考:examples/10_error_handling.cj
2.11 速查手册
代码块结构
main() {
// 条件判断(必须有括号)
if (条件) {
// ...
} else {
// ...
}
// 循环
while (条件) { }
do { } while (条件)
for (变量 in 序列) { }
// 模式匹配
match (值) {
case 模式 => 处理
case _ => 默认
}
}
类型后缀速查
| 类型 | 后缀 |
|---|---|
| 整数 | Int8, Int16, Int32, Int64, IntNative |
| 无符号整数 | UInt8, UInt16, UInt32, UInt64, UIntNative |
| 浮点 | Float16, Float32, Float64 |
| 字符 | Rune |
| 字符串 | String |
| 元组 | (T1, T2, ...) |
| 数组 | Array<T> |
| 区间 | Range<T> |
| 函数 | (T1, T2) -> Rt |
示例参考:examples/11_quick_reference.cj
2.12 集合
仓颉提供了多种集合类型,适用于不同的使用场景:
集合类型
1. Array - 固定大小数组
- 使用场景:不需要增加和删除元素,但需要修改元素
- 字面量:
let arr: Array<String> = ["A", "B", "C"] - 特点:大小固定,内存连续,访问速度快
main() {
// 创建 Array
let arr: Array<String> = ["A", "B", "C"]
// 访问元素(使用下标)
println(arr[0]) // 输出: A
// 修改元素
arr[0] = "X"
println(arr[0]) // 输出: X
// 获取大小
println(arr.size) // 输出: 3
}
2. ArrayList - 动态数组
- 使用场景:需要频繁对元素增删查改
- 特点:大小可变,支持动态添加和删除元素
import std.collection.ArrayList
main() {
// 创建 ArrayList
let list = ArrayList<Int64>()
// 添加元素(使用 add 方法)
list.add(1, at: 0) // 在位置 0 添加 1
list.add(2, at: 1) // 在位置 1 添加 2
// 或使用 add 添加整个数组
let arr = [3, 4, 5]
list.add(all: arr) // list: [1, 2, 3, 4, 5]
// 访问元素(使用 get 方法,返回 Option)
let value = list.get(0)
match (value) {
case Option<Int64>.Some(v) => println("First: ${v}")
case Option<Int64>.None => println("Not found")
}
// 或使用下标访问
println(list[0]) // 输出: 1
// 修改元素
list[0] = 10
// 删除元素
list.remove(at: 0) // 移除第一个元素
// 获取大小
println(list.size)
}
3. HashSet - 哈希集合
- 使用场景:希望每个元素都是唯一的
- 特点:元素唯一,快速查找
import std.collection.HashSet
main() {
// 创建 HashSet
let set = HashSet<Int64>()
// 添加元素
set.add(1)
set.add(2)
set.add(3)
set.add(1) // 重复元素不会被添加
// 检查是否包含
if (set.contains(2)) {
println("包含 2")
}
// 获取大小
println(set.size) // 输出: 3
// 删除元素
set.remove(2)
}
4. HashMap - 哈希映射
- 使用场景:希望存储一系列的映射关系(键值对)
- 字面量:
let map: HashMap<String, Int> = HashMap(("A", 1), ("B", 2))或HashMap([("A", 1), ("B", 2)]) - 特点:键值对存储,快速查找
import std.collection.HashMap
main() {
// 创建 HashMap(使用字面量)
let map: HashMap<String, Int64> = HashMap(
("A", 1),
("B", 2),
("C", 3)
)
// 或者使用方括号
let map2: HashMap<String, Int64> = HashMap([
("A", 1),
("B", 2),
("C", 3)
])
// 或者空 HashMap
let emptyMap = HashMap<String, Int64>()
// 添加元素(使用 add 方法)
emptyMap.add("D", 4)
// 访问元素(使用 get 方法,返回 Option)
let value = map.get("A")
match (value) {
case Option<Int64>.Some(v) => println("A = ${v}")
case Option<Int64>.None => println("Not found")
}
// 或使用下标访问
println(map["A"]) // 输出: 1
// 修改元素
map["A"] = 10
// 检查是否包含键
if (map.contains("B")) {
println("包含键 B")
}
// 删除元素
map.remove("C")
// 获取大小
println(map.size)
}
集合常用方法
添加元素:
- Array:不能添加(大小固定)
- ArrayList:
add(element, at: index)或add(all: array) - HashSet:
add(element) - HashMap:
add(key, value)或map[key] = value
访问元素:
- Array:
array[index]或array.get(index) - ArrayList:
list[index]或list.get(index)(返回 Option) - HashMap:
map[key]或map.get(key)(返回 Option)
修改元素:
- 所有集合都支持
[]下标方式修改
删除元素:
- Array:不能删除
- ArrayList:
remove(at: index) - HashSet:
remove(element) - HashMap:
remove(key)
其他常用方法:
size- 获取元素个数(属性)clear()- 清空所有元素(ArrayList, HashSet, HashMap)contains(element)- 检查是否包含(HashSet, HashMap)get(index)- 获取元素(ArrayList, HashMap,返回 Option)
示例参考:examples/12_collections.cj
2.13 包
仓颉使用包(package)来组织代码,提供模块化和命名空间管理。
包声明
基本语法:
package package.name
目录结构与包声明对应:
src
└── directory_0
├── directory_1
│ ├── a.cj // package demo.directory_0.directory_1
│ └── b.cj // package demo.directory_0.directory_1
└── c.cj // package demo.directory_0
└── main.cj // package demo
示例:
// a.cj
package demo.directory_0.directory_1
public func funcA() {
println("Function A")
}
// b.cj
package demo.directory_0.directory_1
public func funcB() {
println("Function B")
}
// c.cj
package demo.directory_0
public func funcC() {
println("Function C")
}
// main.cj
package demo
import demo.directory_0.directory_1.{funcA, funcB}
import demo.directory_0.funcC
main() {
funcA() // 调用 demo.directory_0.directory_1.funcA
funcB() // 调用 demo.directory_0.directory_1.funcB
funcC() // 调用 demo.directory_0.funcC
}
导入(import)
基本语法:
package currentPackage
import std.math.* // 导入 std.math 的所有内容
import package1.foo // 导入 package1.foo
import {package1.foo, package2.bar, package1.MyClass} // 导入多个
使用示例:
package demo
import std.math.*
import package1.helper
import {package1.MyClass, package2.Utils}
main() {
// 直接使用导入的方法和类型
let result = pow(2, 3) // std.math.pow
helper() // package1.helper
let obj = MyClass() // package1.MyClass
Utils.doSomething() // package2.Utils
}
导入方式
1. 导入所有(通配符):
import std.math.*
// 可以直接使用 std.math 中的所有内容
let value = sqrt(16)
2. 导入特定项:
import std.math.pi
// 只能使用 pi
println(pi)
3. 导入多个项:
import {std.math.sin, std.math.cos}
// 可以使用 sin 和 cos
4. 重命名导入:
import std.math as math
let value = math.sqrt(16)
访问修饰符与包
- private:仅当前文件可见
- internal:当前包及子包可见(默认)
- public:模块内外均可见
示例:
package demo.math
// 默认为 internal,仅 demo.math 及其子包可见
func internalFunc() {
println("internal")
}
// public,任何地方都可访问
public func publicFunc() {
println("public")
}
// private,仅当前文件可见
private func privateFunc() {
println("private")
}
示例参考:examples/13_package_import.cj
第3章:任务处理流程
3.1 代码生成流程
场景1:基础代码生成
需求识别:
- 识别变量类型(let/var)
- 确定数据类型(Int64, String, Rune等)
- 判断是否需要类型标注
生成步骤:
- 加载对应模板(从
templates/目录) - 应用语法规则(对照第2章)
- ⚠️ 检查19个易错点
- 生成最终代码
示例模板:
// templates/basic/variable_declaration.cj.template
{{MODIFIER}} {{VARIABLE_NAME}}: {{TYPE}} = {{INIT_VALUE}}
场景2:函数定义生成
需求识别:
- 函数名和参数列表
- 参数类型和返回类型
- 是否需要命名参数
- 是否需要默认值
生成步骤:
- 加载函数定义模板
- 确定参数是否需要
var修饰(默认let) - 检查返回值类型
- 应用⚠️易错点8(函数参数默认不可变)
场景3:控制流生成
需求识别:
- 条件判断/循环/模式匹配
- 是否需要括号(⚠️必须加括号)
- 是否需要模式匹配
生成步骤:
- 选择合适的控制结构
- ⚠️确保条件表达式有括号(易错点5)
- 检查是否需要
break/continue(不支持标签,易错点6/7) - 生成代码
场景4:class定义生成
需求识别:
- 类名和成员变量
- 构造函数
- 成员函数
- 访问修饰符
生成步骤:
- 加载class模板
- ⚠️应用默认访问修饰符
internal(易错点11) - 生成构造函数
- 生成成员函数
场景5:模式匹配生成
需求识别:
- match表达式或if模式匹配
- 模式类型(常量/通配符/绑定/Tuple/enum/类型)
- 是否需要模式嵌套
生成步骤:
- 选择匹配模式
- ⚠️检查case顺序(按顺序匹配)
- ⚠️确保通配符在最后(易错点9)
- 生成匹配代码
场景6:单元测试生成
需求识别:
- 测试类和测试用例
- 断言类型(Assert/Expect)
- 测试数据
生成步骤:
- 加载测试模板
- 添加
@Test和@TestCase宏 - 选择断言类型
- 生成测试代码
3.2 代码分析流程
场景1:语法正确性分析
分析步骤:
- 解析代码结构
- 对照第2章11个语法模块检查
- ⚠️对照19个易错点清单(第4章)
- 提供修正建议
检查项:
- 变量声明是否正确(let/var)
- 类型标注是否正确或可推断
- 条件表达式是否有括号⚠️
- 字符串插值是否使用
${}⚠️ - 函数参数是否被修改(或明确使用var)⚠️
- 访问修饰符是否符合需求(默认internal)⚠️
场景2:潜在错误检测
分析步骤:
- 识别可能触发19个⚠️易错点的代码模式
- 分析运行时行为
- 提供具体修正建议
重点检测:
- let变量遮蔽⚠️
- Rune转换范围⚠️
- 原始字符串转义⚠️
- break/continue标签跳转⚠️
- Unit类型非法操作⚠️
- match case顺序⚠️
场景3:代码优化建议
分析步骤:
- 检查代码风格和最佳实践
- 提供优化建议
- 推荐使用合适的模板和示例
优化项:
- 是否可以使用Lambda简化代码
- 是否可以使用模式匹配替代复杂的if-else
- 是否应该使用Option类型处理可能的空值
- 是否应该使用enum提高类型安全性
第4章:19个⚠️易错点检查清单
变量相关
⚠️
let不支持变量遮蔽- 错误:在同一作用域重新定义同名let变量
- 修正:使用不同的变量名或使用var
- 参考:第2.1节
⚠️ 函数参数默认是
let不可变的- 错误:在函数内修改参数值
- 修正:使用
var显式声明可变参数 - 参考:第2.4节
⚠️ 成员变量默认访问修饰符是
internal- 错误:误认为默认是
private - 修正:根据需求显式声明访问修饰符
- 参考:第2.8节
- 错误:误认为默认是
控制流相关
⚠️ 所有条件表达式必须有括号
- 错误:
if x > 0 { } - 修正:
if (x > 0) { } - 参考:第2.3节
- 错误:
⚠️ 不支持
goto- 错误:使用goto语句
- 修正:重构代码结构,使用函数或循环控制
- 参考:第2.3节
⚠️
break/continue不支持标签跳转- 错误:
break outerLoop; - 修正:重构嵌套循环逻辑
- 参考:第2.3节
- 错误:
类型转换相关
⚠️ Rune 转整数需确保在有效 Unicode 范围
- 错误:
Rune(0xD800)(代理区字符) - 修正:确保值在
[0x0000, 0xD7FF]或[0xE000, 0x10FFFF] - 参考:第2.2节
- 错误:
⚠️ 原始字符串字面量中转义字符不会被转义
- 错误:认为
##"\n"##会产生换行符 - 修正:使用普通字符串
"\\n"或理解原始字符串特性 - 参考:第2.2节
- 错误:认为
模式匹配相关
⚠️ match 表达式的 case 按顺序匹配
- 错误:将具体case放在通配符之后
- 修正:将具体case放在前面,通配符
_放在最后 - 参考:第2.6节
⚠️ 常量模式不支持字符串插值
- 错误:在match的case中使用
"Hello ${name}" - 修正:使用绑定模式或运行时计算
- 参考:第2.6节
- 错误:在match的case中使用
字符串相关
- ⚠️ 插值字符串使用
${}而非{}- 错误:
"Hello {name}" - 修正:
"Hello ${name}" - 参考:第2.2节
- 错误:
函数相关
- ⚠️ Lambda 表达式可以立即调用
- 说明:
{ => 123 }()是合法语法,立即执行Lambda - 参考:第2.4节
- 说明:
类型系统相关
- ⚠️ Unit 类型只支持赋值、判等、判不等操作
- 错误:对
()进行其他操作(如算术运算) - 修正:理解Unit类型的限制
- 参考:第2.2节
- 错误:对
其他
⚠️ 元组至少需要两个元素
- 错误:定义单元素元组
- 修正:使用具体类型或添加第二个元素
- 参考:第2.2节
⚠️ 区间类型的 step 不能等于 0
- 错误:
Range(1, 10, 0) - 修正:确保step非零
- 参考:第2.2节
- 错误:
⚠️ enum 至少存在一个有名字的构造器
- 错误:定义空enum
- 修正:至少添加一个构造器
- 参考:第2.5节
⚠️ match 表达式的 case 不需要
{}括号- 错误:
case 1 => { println("1") } - 修正:
case 1 => println("1") - 参考:第2.6节
- 错误:
⚠️ Option 类型用于表示可能有值或无值
- 说明:不是所有情况都需要Option,仅当需要明确表示可能无值时使用
- 参考:第2.7节
⚠️ Duration 和 sleep 在
std.core里不需导入- 说明:这些是内置类型和函数,可直接使用
- 参考:第2.10节
第5章:示例与模板参考
5.1 代码示例(examples/)
共39个示例,按难度和语法模块分类:
基础示例(01-15):
- 01_variable_declaration.cj - 变量声明
- 02_basic_types.cj - 基础类型
- 03_control_flow.cj - 控制流
- 04_functions.cj - 函数
- 05_enum.cj - 枚举
- 06_pattern_matching.cj - 模式匹配
- 07_option_type.cj - Option类型
- 08_class.cj - class类型
- 09_unit_test.cj - 单元测试
- 10_error_handling.cj - 错误处理
- 11_quick_reference.cj - 速查手册
- 12-15:更多基础示例
中级示例(16-27):
- Lambda表达式和高阶函数
- 复杂模式匹配
- class继承和多态
- 集合操作
- 异常处理
- 测试用例编写
高级示例(28-39):
- 综合应用案例
- 性能优化技巧
- 最佳实践展示
5.2 代码模板(templates/)
按场景组织:
basic/:
- hello_world.cj.template
- package_structure.cj.template
- main_function.cj.template
functions/:
- function_definition.cj.template
- lambda.cj.template
- higher_order_function.cj.template
control_flow/:
- conditional_logic.cj.template
- loops.cj.template
- error_handling.cj.template
data_structures/:
- class_definition.cj.template
- enum_definition.cj.template
- collection_operations.cj.template
testing/:
- unit_test.cj.template
- test_case.cj.template
第6章:质量控制标准
6.1 代码生成质量检查
生成代码时必须确保:
- ⚠️条件表达式都有括号
- ⚠️字符串插值使用
${} - ⚠️函数参数未被修改(或明确使用 var)
- ⚠️访问修饰符符合需求(默认internal)
- 类型标注正确或可推断
- 模式匹配覆盖所有情况
- ⚠️未触犯19个易错点
6.2 代码分析质量检查
分析代码时必须提供:
- 对照11个语法模块检查
- ⚠️对照19个易错点检查
- 类型系统一致性验证
- 提供具体修正建议
- 标注易错点编号(如 ⚠️易错点5)
6.3 响应质量标准
- 准确性:语法规则必须正确
- 完整性:覆盖所有相关语法点
- 实用性:提供可执行的代码示例
- 可读性:添加清晰的注释和说明
- ⚠️易错点标注:明确标注19个易错点
第7章:MCP文档集成
7.1 MCP工具使用策略
优先级:
- 本SKILL.md(核心语法手册)
- reference/(详细参考手册)
- MCP文档(官方完整文档)
触发条件:
- 查询标准库API(std、stdx)
- 查询高级特性(泛型、并发、宏等)
- 查询官方文档中的最新特性
- 需要更详细的说明和示例
7.2 MCP工具列表
当前可用的MCP工具:
search_documents - 搜索仓颉文档
- 用途:根据关键词搜索相关文档
- 示例:查询"lambda closure 高阶函数"
get_document_content - 获取文档内容
- 用途:获取指定文档的完整内容
- 支持按章节获取(section参数)
list_documents - 列出文档
- 用途:浏览文档结构
- 支持路径导航(类似ls命令)
get_document_overview - 获取文档总览
- 用途:查看分类结构和导航树
- 支持树形视图(view_type="tree")
7.3 使用示例
# 查询lambda相关文档
search_documents(query="lambda closure")
# 获取某个文档的特定章节
get_document_content(
doc_id="manual_source_zh_cn_functions",
section="lambda"
)
# 浏览标准库结构
list_documents(category="libs", subcategory="std")
7.4 文档分类
- manual - 基础手册
- libs - 标准库(std、stdx)
- ohos - OpenHarmony文档
- tools - 开发工具
- extra - 额外内容
附录A:快速参考
A.1 关键字
let、var、if、else、while、do、for、match、case、func、class、enum、init、return、package、import、public、private、protected、internal、static
A.2 操作符
- 算术:加 +、减 -、乘 *、除 /、取模 %
- 比较:相等 ==、不等 !=、小于 <、大于 >、小于等于 <=、大于等于 >=
- 逻辑:与 &&、或 ||、非 !
- 范围:.. 半开区间、..= 闭区间
- 模式匹配:模式绑定 <-、case 分隔 =>
A.3 常用标准库
std.core- 核心类型和函数std.io- 输入输出std.collection- 集合类型std.time- 时间和日期
附录B:版本历史
- v1.0.0 (2025-01-07) - 初始版本
- 包含11个核心语法模块
- 39个代码示例
- 19个⚠️易错点
- 完整的代码生成和分析流程
附录C:贡献指南
欢迎贡献新的示例、模板和改进建议!
贡献方式:
- Fork仓库
- 创建feature分支
- 提交改动
- 发起Pull Request
质量要求:
- 代码必须符合仓颉语法规范
- 示例必须有清晰注释
- 模板必须有使用说明
- 文档必须标注相关语法模块和易错点
附录D:许可证
MIT License
结束
文档维护:仓颉语言社区 最后更新:2025-01-07 版本:v1.0.0