Files
TransPyC/wiki/04-functions.md
T

260 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 04 - 函数定义与调用
Viper 的函数定义使用 Python 的 `def` 语法,通过类型注解指定参数类型和返回类型。
## 基本函数定义
```python
def add(a: t.CInt, b: t.CInt) -> t.CInt:
return a + b
```
等价 C 代码:
```c
int add(int a, int b) {
return a + b;
}
```
## 无返回值函数
```python
def print_msg(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
serial.puts(msg)
```
等价 C 代码:
```c
void print_msg(const char* msg) {
serial_puts(msg);
}
```
## 函数修饰符
通过返回类型的 `|` 组合添加函数修饰符:
### 导出函数
`export` 关键字控制函数的符号可见性——标记为 `export` 的函数不加 SHA1 前缀,裸名导出、全局可见。详见 [01-overview.md 中 t.CExport 深度解析](01-overview.md#tcexport-深度解析)。
```python
export def _start() -> t.CInt:
return 0
```
### 外部函数声明
使用 `extern` 关键字表示仅声明不定义:
```python
extern def isr0() -> t.CVoid: pass
extern def isr1() -> t.CVoid: pass
```
等价 C 代码:
```c
extern void isr0();
extern void isr1();
```
> 注明:`t.State` / `t.CExtern` / `t.CExport` 注解旧写法已废弃(`t.State` 仅 `t.CEnum` 内部临时使用)。
### 内联函数
`t.CInline` 标记函数为内联函数。当前无调用点展开实现,`t.CInline` 仅作存储类标记;实际内联用 `@c.Attribute(t.attr.always_inline())`(期望后续支持)。详见 [01-overview.md 中 t.CInline 深度解析](01-overview.md#tcinline-深度解析)。
```python
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
return a + b
```
### 宏函数
宏函数存在两种形式:`def f(...) -> t.CDefine`,或 `define` 软关键字 `define f(...)`(自动追加 `t.CDefine` 返回注解);存根保留完整函数体;调用点按编译期内联宏展开(TPC 已实现,Test/DefineTest 实证)。详见 [01-overview.md 中 t.CDefine 函数](01-overview.md#tcdefine-函数)。
> 注明:TransPyV 端目前仅识别返回注解为 i32,调用点展开尚未对齐(期望)。
```python
def MAKE_FLAG(bit) -> t.CDefine:
return (1 << bit)
```
### 静态函数
```python
def helper() -> t.CVoid | t.CStatic:
pass
```
## 函数属性装饰器
使用 `@c.Attribute` 添加 GCC `__attribute__` 属性:
```python
@c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16))
def _start() -> t.CInt:
return 0
```
等价 C 代码:
```c
__attribute__((section(".text.startup"), aligned(16)))
int _start() {
return 0;
}
```
### 常用属性组合
```python
@c.Attribute(t.attr.always_inline())
def hot_path() -> t.CInt:
return 0
@c.Attribute(t.attr.noreturn())
def panic(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
serial.puts(msg)
while True: pass
@c.Attribute(t.attr.packed)
```
## LLVM 函数属性
在返回类型注解中通过 `t.attr.llvm` 指定 LLVM 属性:
```python
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
return 0
```
已支持:`always_inline` / `noinline` / `noreturn` / `pure` / `const` / `nounwind` / `noredzone` / `stackrealign` / `willreturn` / `mustprogress`;示例中的 `nobuiltin` 尚未映射(会被静默丢弃,期望支持)。
## 多返回值(CReturn)
使用 `@c.CReturn` 装饰器实现多返回值,返回多个值的函数打包为 LLVM struct 返回,调用处用元组解包 `a, b = f()` 接收:
```python
@c.CReturn(t.CInt, t.CInt)
def divmod(a: t.CInt, b: t.CInt) -> t.CVoid:
q: t.CInt = a // b
r: t.CInt = a % b
return q, r
q, r = divmod(a, b)
```
规则:
1. `CReturn` 中的类型数量决定返回值个数
2. 必须使用左右值(元组解包)赋值,返回值不能直接传入函数。
## 指针参数
```python
def read_data(buf: t.CChar | t.CPtr, size: t.CSizeT) -> t.CInt:
pass
```
等价 C 代码:
```c
int read_data(char* buf, size_t size);
```
### 输出参数模式
使用 `c.Addr` 传递变量地址作为输出参数:
```python
res: t.CInt = fat32.opendir("/", c.Addr(dp))
```
等价 C 代码:
```c
int res = fat32_opendir("/", &dp);
```
后续会使用隐式左值获取来减少 `c.Addr` 的直接使用。
## 函数指针类型
使用 `t.Callable` 定义函数指针类型:
```python
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt]
```
等价 C 代码:
```c
typedef int (*irq_handler_t)();
```
带参数的函数指针:
```python
callback_t: t.Callable[[t.CInt, t.CInt], t.CVoid]
```
## 默认参数
Viper 支持函数默认参数值:
```python
def create_window(x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | str = "Window") -> t.CInt:
pass
```
## return 语句
```python
return 0 # return 0;
return # return; (void 函数)
return a, b # 多返回值(需配合 @c.CReturn)
```
## 全局变量声明
函数外的变量声明为全局变量,函数内通过 `global` 关键字访问:
```python
BootInfo: bootinfo | t.CPtr
def init() -> t.CVoid:
global BootInfo
BootInfo = saved_rdi
```
支持 func-in-func 嵌套函数。
## Call Func 函数调用
函数调用最好通过顺序调用来实现
```python
def add(x: int, y: int, z: int):
return (x + y) * z
export def main() -> int:
print(add(1, 2, 3)) # (1 + 2) * 3 = 9
```
这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。
```python
# 仍然用刚才的 add 举例
export def main() -> int:
print(add(z=3, x=1, y=2)) # (1 + 2) * 3 = 9
```
乱序传参或带参传参对于结构体的初始化同样有效。同时,你也能够给函数指定默认值。
```python
def add(x: int, y: int, z: int = 1):
return (x + y) * z
export def main() -> int:
print(add(1, 2)) # (1 + 2) * 1 = 3
```
在结构体中也可以指定默认值。