← 都来码← 文档索引Objective-C原文English

Objective-C

语法是 C 的超集,UI 接口用法与 C 完全相同。

在手机上怎么跑

vml run examples/objc/test/sysinfo.m

编译要等一会儿(C 那种要一两分钟,脚本类语言几秒)。程序跑起来后屏幕底部就是手柄,方向键 + 四个动作键都在;点右上角返回可以回到命令行。

写法要点

示例

文件演示什么
app/snake.m贪吃蛇(不引头文件的写法看这份)
test/file_io.m读写文件
test/parserexpf_demo.m调用共享库解析表达式
test/sysinfo.m设备信息

同目录还有:demo/demo_std.m、test/libs.m、tty/demo_tty.m、ui/demo_ui.m

实测踩过的坑


下面的内容是从 VML 源码里直接带的(third_party/vml/VMLPrepares/ObjCCompiler/): README 讲这个前端支持什么、怎么编;语言规范 讲语法本身。上游一改,这里重新生成就是最新的。

语言规范

Objective-C 语言编译器规范说明

版本:v1.1 | 日期:2026-07-06 | 修订者:深圳市探索智能科技有限公司

属性值
标准Objective-C 2.0 子集
年份1984 (ObjC 2.0: 2006)
完成度~99%
扩展名.m, .mm
编译器入口ObjCCompiler.Compile() / ObjCCompiler.CompileFile()
源文件Lexer.cs (145 行) / Parser.cs (440 行) / CodeGenerator*.cs (399 行) / ASTNode.cs (76 行)

一、概述

本编译器实现 Objective-C 2.0 的子集,兼容 C 语法,并扩展面向对象特性。核心设计思路是将 C 函数和 ObjC 消息发送统一编译到 VML IR,由运行时库提供 NSObject 等基础类的消息分发骨架。

架构分层:

Objective-C 源码 (.m, .mm)
    → Lexer (词法分析) → Token 流
    → Parser (语法分析) → AST
    → CodeGenerator (代码生成) → VMLProgram (VML IR)
    → VMLAssembler / VMLRuntime → 执行

编译流水线:

  1. 预处理 (Preprocessor) — 展开 #import/#include/#define 及预定义宏
  2. 词法分析 (Lexer) — 源码切割为 Token 流,识别 ObjC 特有 @ 关键字及 C 运算符
  3. 语法分析 (Parser) — 递归下降解析,构建 AST 节点树
  4. 代码生成 (CodeGenerator) — 将 AST 翻译为 VML 指令序列

二、支持的特性

2.1 C 兼容类型

支持基本 C 类型系统。类型关键字由 Lexer.IsTypeName() 集中判定。

类型关键字说明示例
int32 位有符号整数int a = 10;
float32 位浮点float f = 3.14;
double64 位浮点double d = 2.718;
char8 位字符char c = 'A';
void无返回值 / 空类型void func() { }
short16 位整数short s = 100;
long32 位整数(long long 支持 64 位)long l = 99999;
signed有符号修饰signed int x;
unsigned无符号修饰unsigned int u;
struct结构体声明(词法器支持,语法解析器中 struct 可被识别为类型名,但结构体成员访问 ./-> 的语义解析尚未完整实现)struct Point p;
enum枚举声明(词法器识别为关键字,语法解析器将其作为类型名识别,但枚举成员定义尚未实现)enum Color c;
idObjC 通用对象指针,等价于 void *id obj = nil;

类型修饰符组合规则:unsigned / signed 可后接基础类型名(如 unsigned int),解析器递归拼接类型字符串。

2.2 ObjC 关键字

所有 ObjC 关键字以 @ 前缀,由词法器在 @ 字符后读取字母序列并匹配。

2.2.1 核心定义关键字
关键字用途解析情况
@interface声明类接口(实例变量 + 方法声明)完整解析 — 支持类名、父类、实例变量块、方法声明列表
@implementation实现类方法完整解析 — 支持方法体实现
@end终止 @interface / @implementation 块完整解析
@protocol协议声明(词法器识别,语法解析器未实现解析逻辑)仅 Token 可用
@class前向声明类名语法解析器识别并跳过(不生成代码,无语义效果)
@property属性声明(词法器识别,语法解析器未实现解析逻辑)仅 Token 可用
@synthesize属性存取器合成(词法器识别,语法解析器未实现解析逻辑)仅 Token 可用
@dynamic属性存取器运行时提供(词法器识别,语法解析器未实现解析逻辑)仅 Token 可用
@selector选择器字面量(词法器识别,语法解析器未实现解析逻辑)仅 Token 可用

@interface 语法:

@interface MyClass : SuperClass {
    // 实例变量(仅支持类型+名称,不支持指针 * 语法)
    int count;
    float value;
    id delegate;
}
// 方法声明
- (void)doSomething;
- (int)add:(int)a to:(int)b;
@end

@implementation 语法:

@implementation MyClass

// 方法实现
- (void)doSomething {
    count = count + 1;
    return;
}

// C 函数也可混合在 @implementation 中
int helperFunc(int x) {
    return x * 2;
}

@end
2.2.2 消息表达式

支持 Objective-C 经典方括号消息发送语法:

// 基本消息发送
[receiver methodName]

// 带参数的消息发送(参数与选择器标签交织)
[obj setX:10 y:20]

// 无参数消息
[array count]

// 嵌套调用(接收者可为另一消息表达式的结果)
[[self delegate] update]

代码生成:消息发送编译为 CALL objc_<method> 指令,参数和接收者依次 PUSH 到栈上。运行时需提供命名规则为 objc_<selectorName> 的函数实现消息分发。

2.2.3 NSString 字面量

支持 @"string" 语法创建 ObjC 字符串常量:

id greeting = @"Hello, World!";

内部实现:词法器将 @"..." 识别为 ObjCString Token(值为 @"...内容..."),代码生成器将其作为字符串字面量处理,存入数据段并通过 MOVE R0, label(LEA 语义)加载地址。

2.2.4 特殊值
值类型编译为
nil空对象指针MOVE R0, 0
YES布尔真MOVE R0, 1
NO布尔假MOVE R0, 0
self当前对象引用(方法内隐式参数)栈帧偏移 0(R12 基址)
super父类引用(词法器识别,语义未实现)仅 Token 可用
2.3 方法声明

实例方法(-)和类方法(+)均支持:

// 实例方法 — 无参数
- (int)getCount {
    return count;
}

// 实例方法 — 一个参数
- (void)setCount:(int)newCount {
    count = newCount;
}

// 实例方法 — 多个参数(交错选择器标签)
- (int)add:(int)a to:(int)b {
    return a + b;
}

// 类方法(+)
+ (id)sharedInstance {
    return self;
}

方法名编码:选择器中的冒号被替换为下划线。例如 add:to: 编译为标签 objc_add_to_。

2.4 运算符
2.4.1 完整支持的运算符(语法解析器 + 代码生成器均实现)
类别运算符说明
算术+ - * / %加减乘除取模
比较== != < > <= >=等于/不等于/大小比较
赋值= += -= *= /= %=赋值与复合赋值
一元- ! ++ --负号/逻辑非/自增/自减
解引用*指针解引用(词法+语法解析支持,代码生成仅做寄存器间接加载)
取地址&取地址(词法+语法解析支持,代码生成仅做寄存器间接存储)
2.4.2 仅词法器支持(语法解析器未实现语义)
运算符Token 类型说明
&& ||And, Or逻辑与/或(短路求值未实现)
& | ^ ~Amp, Pipe, Caret, Tilde位运算与/或/异或/取反
<< >>LShift, RShift左移/右移
->Arrow结构体指针成员访问
? :Question, Colon三元条件运算符
...Ellipsis可变参数(语法解析器识别但无语义)
2.5 C 函数声明
// 标准函数声明
int add(int a, int b) {
    return a + b;
}

// 无参数函数
void sayHello() {
    return;
}

// 多参数函数
float average(float a, float b, float c) {
    return (a + b + c) / 3.0;
}

// 前向声明(函数体以分号替代)
int factorial(int n);

调用约定:参数通过 PUSH 入栈,CALL func_<name> 调用,RET 返回,返回值在 R0。

函数重载:不支持。函数名在全程序中必须唯一。

2.6 控制流
2.6.1 if / else
if (x > 0) {
    return 1;
} else if (x == 0) {
    return 0;
} else {
    return -1;
}

实现:条件求值后 CMP R0, 0 + JE elseLabel,支持链式 else if。

2.6.2 while
while (i < 10) {
    i = i + 1;
}

实现:循环头标签 + 条件 JE 跳转出口 + 循环尾无条件 JMP 回循环头。

2.6.3 for(C 风格)
for (int i = 0; i < 10; i = i + 1) {
    total = total + i;
}

实现:初始化在循环外执行,条件在每次迭代前求值,更新在循环体后执行。

2.6.4 当前不支持的控制流(词法器已识别关键字)

以下 C 控制流关键字已由词法器定义 Token,但语法解析器未实现解析逻辑:

2.7 注释
// 单行注释

/* 
   多行注释
   支持跨行
*/

两种注释均在词法分析阶段直接丢弃,不进入 Token 流。

2.8 预处理

编译器支持 #import、#include 和 #define 预处理指令。# 开头的行由词法器识别为 Hash Token,语法解析器调用 SkipPreprocessor() 跳过至换行。实际预处理逻辑在 ObjCCompiler.Compile() 入口处由 Preprocessor 类处理:

#import <Foundation/Foundation.h>
#include "common.h"
#define MAX_SIZE 100

三、表达式优先级

递归下降解析器的函数调用链定义了以下优先级(从低到高):

优先级运算符结合性解析函数
1 (最低)= += -= *= /= %=右结合ParseAssignment()
2== != < > <= >=左结合ParseComparison()
3+ -左结合ParseAdditive()
4* / %左结合ParseMultiplicative()
5- ! * & ++ --(前缀)右结合ParseUnary()
6 (最高)字面量、变量、函数调用、消息发送、括号—ParsePrimary()

注意:


四、预定义宏

编译器在启动时自动定义以下宏(见 ObjCCompiler.PredefinedMacros):

宏名值说明
__VML__1标识 VML 编译环境
__VML_VERSION__"1.65.32"VML 工具链版本号
__OBJC__1标识 Objective-C 编译器
__DATE__"Jun 02 2026"(编译时日期)编译日期(MMM dd yyyy 格式)
__TIME__"15:30:45"(编译时时间)编译时间(HH:mm:ss 格式)

这些宏在传递给 Preprocessor 时作为 PredefinedMacros 字典注入,优先于源码中的 #define 生效。


五、AST 节点类型

节点类用途关键字段
ProgramNode顶层程序根节点Statements: List<ASTNode>
FuncDeclNodeC 函数声明/定义Name, ReturnType, Parameters, Body
VarDeclNode变量声明Type, Name, Init
ObjCInterfaceNode@interface 声明Name, SuperClass, Members
ObjCImplNode@implementation 定义Name, Methods
ObjCMethodNodeObjC 方法Name(含冒号), ReturnType, Parameters, Body
ReturnNodereturn 语句Value(可为 null)
IfNodeif / else 语句Condition, ThenBody, ElseBody
WhileNodewhile 循环Condition, Body
ForNodefor 循环Init, Condition, Update, Body
LiteralNode字面量Value(int / float / string)
VarNode变量引用Name
AssignNode简单赋值 =Name, Value
BinaryNode二元表达式Left, Op, Right
UnaryNode一元表达式Op, Operand
CallNodeC 函数调用Name, Arguments
MsgSendNodeObjC 消息发送Receiver, Method(含冒号), Arguments

六、代码生成细节

6.1 寄存器约定

与 VML 内部统一标准一致:

寄存器用途
R0累加器 / 函数返回值 / 第一参数
R1-R3函数参数
R4-R11通用寄存器(调用者保存)
R12帧指针 (BP)(被调用者保存)
R13栈指针 (SP)
R14链接寄存器 (LR)(被调用者保存)
R15返回地址 (RA)(由 CALL/RET 自动管理)
F0-F15单精度浮点寄存器(调用者保存)
D0-D7双精度浮点寄存器(调用者保存)
L0-L764位长整数寄存器(调用者保存)
6.2 函数/方法命名规则
类型标签前缀示例
C 函数func_int main() → func_main
ObjC 方法objc_- (void)doIt:(int)x → objc_doIt_

函数体前生成 JMP skip_<label> 指令以跳过函数体(防止执行流误入),函数体以 RET 结尾。

6.3 变量存储
6.4 消息发送代码生成
[obj setX:10 y:20]

编译为以下 VML 指令序列:

; 参数入栈(逆序先不必要,所有参数按序 PUSH)
MOVE R0, 10
PUSH R0
MOVE R0, 20
PUSH R0
; 接收者入栈
MOVE R0, [R12-{obj_offset}]
PUSH R0
; 调用方法
CALL objc_setX_y_
6.5 self 处理

在 ObjC 方法体内,self 被自动加入符号表,偏移为 0(即帧指针 R12 指向的栈帧顶部)。方法体可使用 self 作为普通变量引用。

6.6 标准库链接

编译文件时 (CompileFile),自动:

  1. 从 #import / #include 指令提取依赖库名
  2. 按 objc 语言类型在 Lib/ 目录下解析库文件
  3. 调用 LibraryLinker 链接外部符号

extractImports 正则同时匹配 #import 和 #include,支持尖括号 <> 和双引号 "" 两种引用形式。


七、已知限制

限制项详细说明
@property 合成词法器识别 @property/@synthesize/@dynamic,但语法解析器完全未实现存取器自动合成逻辑。属性需手工编写 getter/setter 方法
@protocol 协议词法器识别 @protocol Token,语法解析器未实现协议声明、采纳或一致性检查。类型系统不支持 id<Protocol> 语法
Category 分类完全未实现。不支持 @interface ClassName (CategoryName) 扩展语法
Block / Closure完全未实现。不支持 ^ block 语法及闭包捕获
ARC 内存管理不支持自动引用计数,无 strong/weak/unsafe_unretained 等所有权修饰符。需手工管理对象生命周期
属性修饰符@property (nonatomic, strong) 等属性修饰符语法完全未解析
消息转发forwardInvocation: / methodSignatureForSelector: 机制未实现
KVC/KVO键值编码和键值观察机制未实现
异常处理@try @catch @finally @throw 完全未实现(MCU 模式跳过)
struct/enum/union 定义词法器识别关键字,但结构体成员访问 ./-> 的解析与代码生成未实现
逻辑运算符 && / ||Token 已定义,但未纳入表达式解析链,无法使用短路逻辑求值
位运算符& | ^ ~ << >> Token 已定义,但表达式解析器仅支持 & 和 * 作为一元取地址/解引用,不支持二元位运算
三元运算符? : Token 已定义,但语法解析器未实现条件表达式
switch/caseToken 已定义,语法解析器未实现多分支选择
break/continueToken 已定义,语法解析器未实现循环控制
do-whileToken 已定义,语法解析器未实现
goto / 标签Token 已定义,语法解析器未实现
类型转换不支持 C 风格显式类型转换 (type)expr
数组 / 指针运算不支持数组下标 [] 和指针算术
对象分配无 alloc/init 模式或 +new 的编译时支持,需依赖运行时手工提供
super 语义super 关键字仅 Token 可用,无方法查找语义
多文件编译每次调用编译单个 .m 文件,不支持链接时跨文件类层级解析

八、语法参考(EBNF 摘要)

program          := { top_level }

top_level        := objc_interface
                  | objc_implementation
                  | objc_forward_class
                  | function_decl
                  | statement

objc_interface   := "@interface" IDENT [ ":" IDENT ] "{" { ivar_decl } "}" { method_decl } "@end"

objc_implementation := "@implementation" IDENT { method_impl | function_decl } "@end"

objc_forward_class := "@class" IDENT ";"

ivar_decl        := type IDENT ";"

method_decl      := ("-" | "+") type method_selector [ ";"]
method_impl      := ("-" | "+") type method_selector "{" { statement } "}"

method_selector  := IDENT [ ":" "(" type ")" IDENT ] { IDENT ":" "(" type ")" IDENT }

function_decl    := type IDENT "(" [ param_list ] ")" ( ";" | block )

param_list       := type IDENT { "," type IDENT }

block            := "{" { statement } "}"

statement        := return_stmt
                  | if_stmt
                  | while_stmt
                  | for_stmt
                  | var_decl
                  | expression ";"

return_stmt      := "return" [ expression ] ";"

if_stmt          := "if" "(" expression ")" ( block | statement ) [ "else" ( block | statement ) ]

while_stmt       := "while" "(" expression ")" ( block | statement )

for_stmt         := "for" "(" [ var_decl | expression ] ";" [ expression ] ";" [ expression ] ")" ( block | statement )

var_decl         := type IDENT [ "=" expression ] ";"

type             := "int" | "char" | "float" | "double" | "short" | "long" | "long long"
                  | "void" | "id" | "struct" IDENT | "enum" IDENT
                  | "unsigned" type | "signed" type

expression       := assignment

assignment       := comparison { ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" ) assignment }

comparison       := additive { ( "==" | "!=" | "<" | ">" | "<=" | ">=" ) additive }

additive         := multiplicative { ( "+" | "-" ) multiplicative }

multiplicative   := unary { ( "*" | "/" | "%" ) unary }

unary            := ( "-" | "!" | "*" | "&" | "++" | "--" ) unary | primary

primary          := NUMBER
                  | STRING
                  | OBJC_STRING
                  | "nil" | "YES" | "NO"
                  | "[" expression method_selector_exp "]"
                  | IDENT [ "(" [ expression { "," expression } ] ")" ]
                  | "(" expression ")"

九、编译命令示例

# 编译单个 .m 文件
dotnet run --project VMLTool -- input.m -o output.vml

# 编译 .mm 文件
dotnet run --project VMLTool -- app.mm -o app.vml

# 使用 ObjCCompiler 直接编译(编程接口)
ObjCCompiler.Compile("int main() { return 0; }")
ObjCCompiler.CompileFile("MyClass.m", includePaths, libraryPaths)

十、版本历史

版本日期变更
1.02025-12初始实现 — 词法器 + 递归下降解析器 + VML 代码生成
1.65.17+2026-05集成 Preprocessor、__OBJC__ 预定义宏、ExtractImports 链接支持

🆕 字符串编码 (v1.65.19)

该语言编译器通过共享库 (Lib/shared/) 间接使用 VML 字符串体系。

伪指令宽度编码C 类型
.string8-bitUTF-8char*
.wstring16-bitUTF-16LEwchar_t*
.ustring32-bitUTF-32LEchar32_t*

MCU 模式 (默认): 字符串输出为 UTF-8 (.string) OS 模式: 可通过 VML_WSTRING 宏判断编码

共享库已提供宽字符串转换函数 (wchar.h/uchar.h),各语言编译器可按需使用。

编译器 README

Objective-C 编译器

路径: VMLPrepares/ObjCCompiler/ 完成度: ~99% | 🟢 生产可用 标准库: Lib/objc/ 依赖: CCompiler (ObjC 是 C 的超集)

功能

编译模式

MCU 模式(默认 --mode mcu)

ObjC 汇编为目标保留 C 兼容子集。

跳过:

保留:

OS 模式(--mode os)

OS 模式支持全部语言特性。

RAM 级别

使用

dotnet run --project VMLTool -- input.m -o output.vml

测试