R
向量语言,写法与桌面 R 接近。
在手机上怎么跑
vml run examples/r/test/sysinfo.r
编译要等一会儿(C 那种要一两分钟,脚本类语言几秒)。程序跑起来后屏幕底部就是手柄,方向键 + 四个动作键都在;点右上角返回可以回到命令行。
写法要点
- 直接调用
ui_* - 状态可以放向量里
示例
| 文件 | 演示什么 |
|---|---|
test/catch.r | 接方块 |
test/file_io.r | 读写文件 |
test/parserexpf_demo.r | 调用共享库解析表达式 |
test/sysinfo.r | 设备信息 |
同目录还有:demo/demo_std.r、test/libs.r、tty/demo_tty.r、ui/demo_ui.r
下面的内容是从 VML 源码里直接带的(third_party/vml/VMLPrepares/RCompiler/): README 讲这个前端支持什么、怎么编;语言规范 讲语法本身。上游一改,这里重新生成就是最新的。
语言规范
R 语言编译器规范说明
版本:v1.0 | 日期:2026-07-06 | 修订者:深圳市探索智能科技有限公司
元数据
| 项目 | 说明 |
|---|---|
| 标准 | R 4.x 子集 (S 语言兼容) |
| 语言诞生年份 | 1993 |
| 完成度 | ~96% |
| 文件扩展名 | .r |
| 编译器入口 | RCompiler.RCompiler.Compile() |
| 插件注册 | RCompilerPlugin : CompilerPluginExBase |
| 语言分类 | 统计分析语言 |
概述
R 语言是一种用于统计计算和图形展示的编程语言和环境。本编译器实现了 R 语言的核心语法子集,支持向量化数据操作、统计函数、控制流和自定义函数定义。编译产物为 VML 汇编,可进一步翻译到 16 种目标架构。
编译器架构遵循标准 VML 前端模式:
R 源码 (.r) → Lexer (Lexer.cs) → Token 流
→ Parser (Parser.cs) → AST (ASTNode.cs)
→ CodeGenerator (CodeGenerator*.cs) → VML 程序
入口文件 RCompiler.cs 编排整个编译流水线,包括预处理、词法分析、语法分析、代码生成和标准库链接。
支持的特性
数据类型
| 类型 | R 表示 | 内部表示 | 说明 |
|---|---|---|---|
numeric | 42, 3.14, 1.5e-10 | int / float | 整数和浮点数。R 中 integer 可加 L 后缀(如 42L),解析时 L 被去除 |
character | "hello", 'world' | string (data section) | 字符串字面量,支持双引号、单引号、反引号三种定界符 |
logical | TRUE, FALSE | int (1 / 0) | 布尔值映射为整数 |
NULL | NULL | int (0) | 空值,R0 置零 |
NA | NA | int (int.MinValue) | 缺失值,使用哨兵值表示 |
Inf | Inf | float (float.PositiveInfinity) | 无穷大 |
注意:当前实现中 numeric 的 integer 和 float 在运行时不做严格类型区分。科学计数法(如 1.5e-10、2E+3)被完整支持。
赋值运算符
R 支持三种赋值形式,优先级从高到低:
# 1. 左赋值(推荐风格)
x <- 42
y <- c(1, 2, 3)
# 2. 等号赋值(函数参数风格)
x = 42
# 3. 超级赋值(父环境赋值)
x <<- 42
<-和=在当前作用域分配变量<<-标记为超级赋值,用于向父作用域写入(当前编译器实现中与<-行为等价,作用域层级尚未完全实现)- 赋值表达式返回右值,支持链式赋值:
a <- b <- 10
算术运算符
| 运算符 | 语法 | 说明 |
|---|---|---|
| 加法 | a + b | 数值相加 |
| 减法 | a - b | 数值相减 |
| 乘法 | a * b | 数值相乘 |
| 除法 | a / b | 浮点除法 |
| 幂运算 | a ^ b | 指数运算 |
| 取模 | a %% b | 整数取余(注意必须为两个 % 符号) |
比较运算符
| 运算符 | 语法 | 说明 |
|---|---|---|
| 等于 | a == b | 相等比较 |
| 不等于 | a != b | 不等比较 |
| 小于 | a < b | 小于比较 |
| 大于 | a > b | 大于比较 |
| 小于等于 | a <= b | 小于或等于 |
| 大于等于 | a >= b | 大于或等于 |
比较结果返回整数 1 (TRUE) 或 0 (FALSE)。
逻辑运算符
| 运算符 | 语法 | 说明 |
|---|---|---|
| 逻辑与 | a & b | 短路求值 (映射为 &&) |
| 逻辑或 | a | b | 短路求值 (映射为 ||) |
| 逻辑非 | !a | 一元取反 |
注意:当前实现不区分向量化的 &/| 和短路求值的 &&/||,所有逻辑运算均执行短路求值。R0 = 0 视为 FALSE,非零视为 TRUE。
函数
# 函数定义
add <- function(a, b) {
return(a + b)
}
# 无参函数
greet <- function() {
return("Hello")
}
# 隐式返回:函数体最后一个表达式的值即为返回值
square <- function(x) {
x * x
}
- 使用
function(params) { body }语法定义匿名函数,通过<-赋值给变量名 - 参数列表可以为空
return(expr)提前返回,return expr(无括号)也受支持- 函数体最后一个表达式的值隐式作为返回值
- 函数调用:
name(arg1, arg2, ...),参数从右向左压栈,返回值在 R0 寄存器 - 函数内部有独立的变量作用域(参数压入当前栈帧,函数退出时恢复)
控制流
if / else
if (x > 0) {
print("positive")
} else {
print("non-positive")
}
# else if 链(嵌套解析)
if (x > 0) {
print("positive")
} else if (x < 0) {
print("negative")
} else {
print("zero")
}
条件表达式求值后与 0 比较:CMP R0, 0; JE。非零为真。
while 循环
i <- 1
while (i <= 10) {
print(i)
i <- i + 1
}
前测试循环:条件为假时跳过循环体。
for 循环
for (i in 1:5) {
print(i)
}
for (x in c(10, 20, 30)) {
print(x)
}
for (variable in sequence) { body }语法- 迭代变量在每次循环中被赋予序列的当前元素值
- 支持
c()向量和范围表达式 - 序列长度在编译时确定(仅支持常量长度序列)
repeat 循环
repeat {
# 必须包含 break 条件,否则为无限循环
if (condition) {
break
}
}
无限循环,必须使用 break 显式退出。
break 和 next
# break: 退出最内层循环
for (i in 1:10) {
if (i > 5) {
break
}
}
# next: 跳过当前迭代,进入下一次循环
for (i in 1:10) {
if (i %% 2 == 0) {
next
}
print(i) # 只打印奇数
}
break跳转到循环结束标签next跳转到循环条件检查标签(等价于 C 语言continue)- 在循环外使用会导致编译错误
向量 (Vectors)
# c() 构造函数
x <- c(1, 2, 3, 4, 5)
y <- c(TRUE, FALSE, TRUE)
z <- c() # 空向量,返回 NULL
# 范围运算符
1:10 # 整数序列,解析为特殊调用
- 向量通过
c()函数构造,在解析器中作为SeqNodeAST 节点特别处理 - 向量在堆上分配(
SYSCALL 2= malloc),每个元素占 4 字节 - 元素按顺序存储在连续内存中
- 空向量
c()返回 0 (NULL) - 范围运算符
start:end被解析为对:函数的调用 - 不支持:命名向量、类型强制转换、向量回收(recycling)
索引
x <- c(10, 20, 30, 40)
a <- x[1] # 10 (R 索引从 1 开始)
b <- x[3] # 30
- 使用
[index]语法,1-based 索引 - 内部实现:索引减 1,乘以 4(字偏移),从基址加载
- 索引表达式可以是任意表达式:
x[i + 1] - 不支持:负索引(排除)、逻辑索引、多维索引、
[[提取运算符、切片x[2:4]
注释
# 这是单行注释
x <- 1 # 行尾注释
仅支持 # 开头的单行注释。不支持多行注释。
科学计数法
x <- 1.5e-10 # 1.5 × 10^-10
y <- 2.0E+5 # 200000
z <- 3e8 # 300000000
词法分析器完整解析科学计数法:尾数、小数点、e/E、可选符号、指数。
标识符规则
- 以字母、下划线或点号开头(点号后必须跟字母或下划线)
- 后续字符:字母、数字、下划线、点号
- 区分大小写:
x和X是不同的变量 - 关键字不可作为标识符使用
- 示例合法标识符:
x,_private,.hidden,my.var,data_1
source() 导入
source("lib/utils.r")
source() 调用以正则表达式从源码中提取(预处理阶段前解析),将指定的 .r 文件内容链接到当前程序。这是一个编译时机制,非运行时函数调用。
表达式优先级表
优先级从高到低排列(数字越小优先级越高):
| 优先级 | 运算符 | 结合性 | 说明 |
|---|---|---|---|
| 1 | () [] $ : f() | 左结合 | 分组、索引、成员访问、范围、函数调用 |
| 2 | - (一元) ! + (一元) | 右结合 | 一元负号、逻辑非、一元正号 |
| 3 | ^ | 右结合 | 幂运算 |
| 4 | * / %% | 左结合 | 乘、除、取模 |
| 5 | + - | 左结合 | 加、减 |
| 6 | == != < > <= >= | 左结合 | 比较运算符 |
| 7 | & | 左结合 | 逻辑与 |
| 8 | | | 左结合 | 逻辑或 |
| 9 | <- <<- = | 右结合 | 赋值 |
预定义宏
编译器在预处理阶段(Preprocessor)注入以下宏:
| 宏名称 | 典型值 | 说明 |
|---|---|---|
__VML__ | 1 | 指示代码正在 VML 工具链中编译 |
__VML_VERSION__ | "1.65.32" | VML 工具链版本号 |
__R__ | 1 | 指示当前编译语言为 R |
__DATE__ | "Jun 02 2026" | 编译日期 |
__TIME__ | "15:30:00" | 编译时间 |
这些宏可以通过预处理器指令 #ifdef、#ifndef、#if 等使用,支持条件编译。
限制与不支持的特性
当前实现为🟢 生产可用级别,以下 R 语言核心特性尚未支持:
对象系统
- S3 对象:类和泛型函数机制(
class<-、UseMethod、NextMethod) - S4 对象:正式类定义和方法分派(
setClass、setMethod) - RC (Reference Classes):引用类
- R6 类:现代 OOP 系统
数据结构
- list():递归列表(仅实现了
c()原子向量) - data.frame:表格数据结构
- matrix:多维矩阵(
matrix()、%*%矩阵乘法) - array:多维数组(
array()、dim<-) - factor:分类变量
- names / attributes:属性系统(
names<-、attr<-、attributes<-) - 环境 (environment):环境对象和词法作用域层级
函数式编程
- apply 家族:
apply()、lapply()、sapply()、tapply()、mapply()等 - 闭包:词法作用域中捕获自由变量的函数
...(dots):可变参数on.exit():函数退出钩子invisible():不可见返回
公式接口
- 公式对象:
y ~ x1 + x2(统计建模核心语法) - 相关函数:
lm()、glm()、terms()、model.frame()
向量化运算
- 向量回收 (recycling):不等长向量运算时的自动扩展
- 逐元素运算:所有运算符当前为标量操作,不自动对向量广播
- 向量化 ifelse():条件向量选择
包和库
- library() / require():包加载机制
- CRAN 包:任何第三方包
- 命名空间:
::和:::运算符 - base/recommended 包:R 自带的统计和图形包
其他
- 管道运算符:
|>(原生管道) 和%>%(magrittr) - switch():多分支选择
- tryCatch():异常处理
- R 复数类型:
complex - R 原始类型:
raw - 字符串操作函数:
paste()、sprintf()、gsub()等(需通过 C 共享库手动实现) - 绘图系统:图形设备接口
- S4 泛型:
setGeneric、setMethod - 延迟求值:R 的惰性参数求值语义
- R 的
[[[$作为函数:运算符的函数形式 - 负索引与逻辑索引:
x[-1]、x[x > 5] - 列表命名元素:
x[["name"]] - formals() / body():函数对象内省
编译器实现细节
文件结构
VMLPrepares/RCompiler/
├── Token.cs # Token 类型定义与值载体
├── Lexer.cs # 词法分析器(继承 LexerBase)
├── ASTNode.cs # 抽象语法树节点定义
├── Parser.cs # 递归下降语法分析器
├── CodeGenerator.cs # 代码生成主入口
├── CodeGenerator.Expressions.cs # 表达式代码生成
├── CodeGenerator.Statements.cs # 语句代码生成
├── RCompiler.cs # 编译器入口与流水线编排
├── RCompilerPlugin.cs # 插件注册
├── Program.cs # CLI 入口
└── RCompiler.csproj # 项目文件
寄存器约定
遵循 VML 标准调用约定:
| 寄存器 | 用途 |
|---|---|
| R0 | 累加器 / 函数返回值 |
| R12 | 帧指针 (BP) |
| R13 | 栈指针 (SP) |
| R15 | 返回地址 (RA) |
内存模型
- 局部变量:在栈帧内分配,通过
R12-offset寻址 - 全局变量:在 data section 中分配,通过标签
var_name寻址 - 未知变量首次赋值时自动在栈上分配空间(4 字节对齐)
- 向量元素:堆分配(
SYSCALL 2/ malloc),返回基址指针
函数调用约定
- 参数从右向左压栈
CALL func_<name>跳转到函数体标签- 被调函数:
- 序言:
PUSH R15; PUSH R12; MOVE R12, R13 - 体:执行语句。参数在栈帧内通过
R12-<offset>访问 - 尾声:
POP R12; RET
- 序言:
- 返回值通过 R0 传递
字符串处理
- 字符串字面量存储在 data section 中,带唯一标签
str_<guid> - 使用
MOVE R0, label将字符串地址加载到 R0 - 重复字符串共享同一标签(通过
_stringCache字典去重)
标准输出
当前编译器无内置 print() 实现。输出需通过 VML SYSCALL 或 BIOS 调用实现,或链接 C 共享库中的 printf 函数。
R 转换为 VML 的典型映射
| R 构造 | VML 指令序列 |
|---|---|
x <- 10 | MOVE R0, 10; MOVE [R12-x], R0 |
a + b | MOVE R0, [R12-a]; MOVE R0, [R12-b]; ADD R0, R0, R0 |
if (x) { ... } | MOVE R0, [R12-x]; CMP R0, 0; JE else_label; ...; LABEL else_label |
while (x) { ... } | LABEL while_start; MOVE R0, [R12-x]; CMP R0, 0; JE wend; ...; JMP while_start; LABEL wend |
for (i in c(1,2,3)) { ... } | 索引初始化 + 条件检查循环 + 每次迭代加载序列元素值 |
return(x) | MOVE R0, [R12-x]; POP R12; RET |
f(x, y) | MOVE R0, [R12-y]; PUSH R0; MOVE R0, [R12-x]; PUSH R0; CALL func_f |
v[i] | 计算基址 + (i-1)*4,间接加载 |
c(1, 2, 3) | SYSCALL 2 (malloc 12) + 依次存储元素 + 恢复基址到 R0 |
🆕 字符串编码 (v1.65.19)
该语言编译器通过共享库 (Lib/shared/) 间接使用 VML 字符串体系。
| 伪指令 | 宽度 | 编码 | C 类型 |
|---|---|---|---|
.string | 8-bit | UTF-8 | char* |
.wstring | 16-bit | UTF-16LE | wchar_t* |
.ustring | 32-bit | UTF-32LE | char32_t* |
MCU 模式 (默认): 字符串输出为 UTF-8 (.string) OS 模式: 可通过 VML_WSTRING 宏判断编码
共享库已提供宽字符串转换函数 (wchar.h/uchar.h),各语言编译器可按需使用。
编译器 README
R 编译器
路径: VMLPrepares/RCompiler/ 完成度: ~96% | 🟢 生产可用 标准库: Lib/r/
功能
- ✅ 词法分析 + 语法分析 + 代码生成 (Lexer/Parser/CodeGenerator)
- ✅ 控制流 (if/else/for/while/repeat/break/next)
- ✅ 函数: function() 定义/参数/return/匿名函数
- ✅ 赋值: <- / <<- / = 运算符
- ✅ 数据结构: c() 向量 / list() / 公式 ~ 运算符
- ✅ %in% 运算符
- ✅ 科学计数法数值
- ✅ 31 测试 0 失败
- ⚠️ S3/S4 对象系统 — 未实现
- ⚠️ data.frame/matrix — 未实现
- ✅ C 风格预处理 (#ifdef/#ifndef/#define/#include)
编译模式
MCU 模式(默认 --mode mcu)
MCU 模式针对单片机/裸机环境优化。
跳过:
- source() 动态加载
- S3/S4 对象系统
- 图形设备
保留:
- 基本统计运算
- 向量操作 (c/seq/rep)
- print 映射到 UART
OS 模式(--mode os)
OS 模式支持全部语言特性。
RAM 级别
--ram k:KB级别--ram m:MB级别(默认)--ram g:GB级别
使用
dotnet run --project VMLTool -- input.r -o output.vml
测试
VMLTests/NewCompilerTests.cs— 31 测试 (0 失败)VMLTests/FullPipelineTests.cs— 全管线测试Examples/r/— 示例程序 (start/factorial/file_io/info)