# vern **Repository Path**: wndproc12/vern ## Basic Information - **Project Name**: vern - **Description**: Vern —— 用自然英语编写,直接生成 Linux ELF 的零依赖编译器。没有 libc,没有 C/C++,从 .v 源码直达可执行文件。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-27 - **Last Updated**: 2026-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Vern 编译器 Vern 是一门"用自然英语编程"的语言。本仓库为其编译器实现: 从 `.v` 源文件**直接生成 Linux x86-64 ELF 可执行文件**(经 GAS 汇编 + ld 链接, 不依赖 libc,不经过 C/C++ 中间表示)。 支持变量与类型注解、整数/浮点/布尔/字符串/`byte` 类型、`array[T, N]` 定长数组、 算术与比较运算(符号与自然语言单词运算符并存)、逻辑运算、`if/elif/else` 与 `while`(花括号形式与 `then/end`、`do/end` 自然语言形式)、`print` 及其别名 `say/show/display`、单/双引号字符串、顶层函数定义与调用、Linux 系统调用、 高层文件 I/O、**指针**(`*T` / `&x` / `*p`)、**位运算**(`&`/`|`/`^`/`~`/`<<`/`>>`)。 浮点输出采用手写的最短往返 dtoa。 ## 依赖 - Linux x86-64 - `g++`(支持 C++17) - GNU binutils:`as`、`ld` ## 构建 ```sh make # release 版(-O2),生成 bin/vern make debug # 调试版(-g -O0) make clean # 清理 build/ 与 bin/ ``` ## 使用 ```sh ./bin/vern hello.v # 生成 a.out ./bin/vern hello.v -o hello # 生成 hello ./hello # 运行,打印文本到 stdout ``` ### 选项 | 选项 | 说明 | |---|---| | `-o ` | 指定输出可执行文件名(默认 `a.out`) | | `--dump-tokens` | 打印 Token 列表 | | `--dump-ast` | 打印 AST 树 | | `--verbose` | 等同 `--dump-tokens --dump-ast` | | `--help` | 显示帮助 | ## 语言特性 ### 输出 - `print` / `say` / `show` / `display` 互为别名,输出后换行。大小写不敏感。 ### 字符串 - 字符串字面量可用双引号 `"..."` 或单引号 `'...'` 包裹,两者语义完全等价。 - 支持 C 风格转义:`\n \t \r \\ \" \' \0 \xHH` 等。 ### 变量与类型 - `let x = 10` 声明;`let x: int = 10` 带类型注解(`int/float/string/bool`)。 - 赋值可用 `=`、`is`、`be`、`are`(如 `x = x + 1`、`x is x + 1`)。 - 无类型注解时由初始值推断。 ### 运算符 - 算术:`+ - * /` 与单词 `plus minus times over` 等价。 - 比较:`< > <= >= == !=` 与短语 `is/are/be (not)? (greater|less) than (or equal to)?`、 `is/are/be (not)? equal to`、`is/are/be (not)?` 等价。 - 逻辑:`and or not`。 ### 控制流 - `if cond { ... } elif cond { ... } else { ... }`(花括号形式)。 - `if cond then ... elif cond then ... else ... end`(自然语言形式)。 - `while cond { ... }` 或 `while cond do ... end`。 - `if` 和 `while` 的块开头关键字 `then` 与 `do` 可互换使用 (如 `if cond do ... end`、`while cond then ... end`)。 ### 注释 - `//` 行注释与 `/* */` 块注释。 ### 未声明标识符语法糖 - `print`/`say`/`show`/`display` 后跟未声明标识符时,视为字符串字面量 (如 `print hello` 等价于 `print "hello"`),编译时发警告到 stderr。 已声明的变量仍按变量值打印。 ### 函数 - 符号形式:`fn f(a: int, b: int) -> int { ... }`。 - 自然形式:`function f with a: int and b: int returns int then ... end`。 - 返回类型用 `-> T` 或 `returns T`(与参数形式正交);省略则为 void。 - `return [expr] [from function]`;裸 `return` 仅 void 函数允许。 - 调用:符号形式 `f(a, b)`;自然形式 `f with a and b`(参数用 `and` 分隔)。 无参调用须用 `f()`。 - 参数与返回类型仅支持 `int`/`float`/`bool`(暂不支持 string)。最多 6 个参数。 - 若定义 `function main`,`_start` 在执行顶层语句后调用它,其 int 返回值作 exit code。 ### byte 类型与数组 - `byte`:u8(0–255),数值类型,与 `int` 双向隐式转换。 - `array[T, N]`:定长数组,栈内联。仅支持 `byte`/`int`/`float`/`bool` 元素。 声明:`let buf: array[byte, 16]`(无初始化表达式)。 - 元素访问(双语法): - 读:`element i of arr` 或 `arr[i]`。 - 写:`set element i of arr to V` 或 `arr[i] = V`。 - `length of X`:对数组返回编译期 N,对 string 返回运行时长度。 ### Linux 系统调用 三层语法(不依赖 libc,直接发射 `syscall` 指令): - 通用数字形式:`invoke syscall N with args a, b, ...` 或 `syscall(N, a, b, ...)`。 - 命名变体: - `syscall write to fd F from BUF [size N]`(write=1) - `syscall read from fd F into BUF up to N bytes`(read=0) - `syscall exit with code N`(exit=60) - 字符串可作 write 缓冲区,省略 `size` 时按字符串长度写入;数组缓冲区须显式 `size`。 ### 文件 I/O 高层 API(不使用 `syscall` 关键字),双语法: - 自然形式: - `open PATH for (reading|writing|appending)` → int fd - `close FD` - `write BUF to FD [size N]` - `read from FD into BUF up to N bytes` → int(字节数) - 符号形式(syscall 顺序:fd, buf, count): - `open(PATH, MODE)` / `write(FD, BUF [, N])` / `close(FD)` / `read(FD, BUF, N)` - `MODE` 为裸关键字 `reading`/`writing`/`appending`。 - `open` 是表达式(返回 fd),`close`/`write` 是语句,`read` 是表达式(返回字节数)。 - fd 为普通 `int`;write 的 BUF 可为 string 或 array,read 的 BUF 必须为 array。 ### 指针 双语法: - 类型:`*T`(符号形式)或 `ptr to T`(自然形式)。 - 取址:`&x`(符号形式)或 `address of x`(自然形式)。 - 解引用读:`*p`(符号形式)或 `value at p`(自然形式)。 - 解引用写:`*p = v`(符号形式)或 `set value at p to v`(自然形式)。 - 可取址的左值为变量引用、数组元素访问、解引用表达式;字面量和临时值不可取址。 - 数组传参时隐式退化为指向首元素的指针。 - 指针算术暂不支持(`p + 1`、`p[i]` 等留待后续)。 ### 位运算 6 个符号运算符(无自然语言对应形式): - `&`(按位与)、`|`(按位或)、`^`(按位异或)、`~`(按位取反)、`<<`(左移)、`>>`(右移)。 - 仅接受 `int`/`byte` 操作数(byte 隐式提升为 int),结果恒为 `int`。 - 优先级(Python 风格,高于比较):`shift > bitand > bitxor > bitor`。 - `flags & 0x08 == 0` 解析为 `(flags & 0x08) == 0`,避免 C 的陷阱。 - 所有位运算均左结合。 示例: ```v // 算术与变量 let x: int = 10 let y = x plus 5 print y times 2 // 自然语言 if if y is greater than 20 then say "big" else say "small" end // while 循环 let i = 0 while i < 3 do print i i = i + 1 end // 浮点(最短往返输出) print 3.14 print 1.0 over 3.0 // 函数(双语法) fn add(a: int, b: int) -> int { return a + b } print add(3, 4) // 数组与 byte let buf: array[byte, 4] set element 0 of buf to 72 set element 1 of buf to 105 buf[2] = 10 syscall write to fd 1 from buf size 3 // 文件 I/O(符号形式) let f = open("/tmp/hello.txt", writing) write(f, "hi, file!\n") close(f) let g = open("/tmp/hello.txt", reading) let rbuf: array[byte, 64] let n = read(g, rbuf, 64) close(g) syscall write to fd 1 from rbuf size n ``` ## 测试 ```sh make test # 构建并运行测试套件 # 或 ./tests/run_tests.sh ``` 测试用例位于 `tests/cases/`,每个 `.v` 文件配对一个 `.expected.txt`。 ## 架构 ``` src/ ├── main.cpp 驱动:CLI 解析 → Lexer → Parser → Sema → Codegen ├── lexer/ 词法分析(Token、转义、注释) ├── parser/ 递归下降语法分析(AST + 调试打印) ├── ast/ AST 节点与 Visitor 接口 ├── sema/ 语义分析(ErrorReporter 累积错误) ├── codegen/ 代码生成(抽象接口 + x86-64 Linux 汇编后端) └── utils/ Arena 分配器、错误处理、CLI 解析、源定位 ``` - **AST 内存**:统一由 Arena 分配,整体释放。 - **错误处理**:Lexer/Parser 抛 `CompileError` 异常;Sema 用 `ErrorReporter` 累积。 每个错误/警告附带错误码(如 `V0001`),输出格式 `error [V0001]: file:line:col: msg`。 - **后端**:`CodeGenerator` 抽象接口,当前实现 `AsmBackend`(x86-64 Linux), 通过 `fork` + `execvp` 调用 `as` 与 `ld`。预留跨平台扩展点。 ## 错误码 每个编译错误与警告均附带 `V` + 4 位数字的错误码,前两位标识模块: | 区间 | 模块 | 类型 | |---|---|---| | V00xx | Lexer | 词法错误(快速失败) | | V01xx | Parser | 语法错误(快速失败) | | V02xx | Sema | 语义错误(累积) | | V03xx | Sema | 语义警告(累积,不阻断编译) | | V04xx | Backend | 后端/链接错误(快速失败) | | 码 | 含义 | |---|---| | V0001 | unterminated string literal | | V0002 | newline in string literal | | V0003 | unterminated block comment | | V0004 | unterminated escape | | V0005 | invalid `\x` escape | | V0006 | unknown escape sequence | | V0007 | expected `!=` | | V0008 | unexpected character | | V0101 | expected X(通用期待某 token) | | V0102 | expected statement | | V0103 | expected expression | | V0104 | expected type name | | V0105 | expected `=`/`is`/`be`/`are` in let | | V0106 | expected `{`/`then`/`do` after if condition | | V0107 | expected `{`/`do`/`then` after while condition | | V0108 | expected `{` after else | | V0109 | expected `{`/`then`/`do` after function signature | | V0110 | nested array types are not supported | | V0113 | array size must be an integer literal | | V0116 | syscall number must be an integer literal | | V0118 | expected '(', 'write', 'read', or 'exit' after 'syscall' | | V0119 | expected 'reading', 'writing', or 'appending' after 'for' in open | | V0201 | redeclaration of variable | | V0202 | use of undeclared variable | | V0203 | assignment to undeclared variable | | V0204 | cannot initialize X variable with Y value | | V0205 | cannot assign X to Y variable | | V0206 | unary `-` requires numeric | | V0207 | `not` requires bool | | V0208 | operator requires numeric operands | | V0209 | operator requires same-type operands | | V0210 | operator requires bool operands | | V0211 | if condition must be bool | | V0212 | while condition must be bool | | V0213 | redeclaration of function | | V0214 | call to undeclared function | | V0215 | argument count mismatch | | V0216 | argument type mismatch | | V0217 | return type mismatch / missing return value | | V0218 | cannot return value from void function | | V0219 | too many arguments (max 6) | | V0220 | `return` outside function | | V0221 | index of non-array | | V0222 | array index must be int | | V0223 | length of non-array/non-string | | V0224 | cannot assign X to Y array element | | V0225 | too many syscall arguments (max 6) | | V0226 | syscall argument must be int/byte/bool | | V0227 | write buffer must be string or array | | V0228 | read buffer must be array | | V0229 | write array buffer requires explicit size | | V0230 | open path must be string | | V0231 | file descriptor must be int/byte/bool | | V0232 | file write buffer must be string or array | | V0233 | file read buffer must be array | | V0234 | file write array buffer requires explicit size | | V0240 | cannot dereference non-pointer | | V0241 | cannot assign X to Y via pointer | | V0242 | cannot take address of non-lvalue | | V0243 | pointer type mismatch in argument | | V0244 | bitwise operator requires int/byte operands | | V0245 | unary `~` requires int/byte | | V0301 | unquoted string 警告 | | V0401 | pipe failed | | V0402 | fork failed | | V0403 | execvp failed | | V0404 | cannot open file for writing | | V0405 | failed writing | | V0406 | as failed | | V0407 | ld failed |