← Dolaima← Docs indexObjective-C原文中文

Objective-C

The syntax is a superset of C, and the UI interfaces are used exactly as in C.

How to run it on your phone

vml run examples/objc/test/sysinfo.m

Compiling takes a while (one to two minutes for C, a few seconds for scripting languages). Once the program is running, the gamepad is at the bottom of the screen - the D-pad and the four action buttons are all there. Tap the back arrow in the top-right corner to return to the Shell.

What to know before you write

Examples

FileWhat it shows
app/snake.mSnake (see this one for the style that includes no header)
test/file_io.mReading and writing files
test/parserexpf_demo.mCalling a shared-library expression parser
test/sysinfo.mDevice information

Also in this folder: demo/demo_std.m, test/libs.m, tty/demo_tty.m, ui/demo_ui.m

Pitfalls we hit on real devices


The rest of this page is taken straight from the VML source (third_party/vml/VMLPrepares/ObjCCompiler/): the README covers what this front end supports and how to compile it, and the language reference covers the syntax itself. When upstream changes, regenerating this page brings it up to date.

Language reference

Objective-C Language Compiler Specification

Version: v1.1 | Date: 2026-07-06 | Revised by: Shenzhen Tanso Intelligent Technology Co., Ltd.

AttributeValue
StandardObjective-C 2.0 subset
Year1984 (ObjC 2.0: 2006)
Completeness~99%
Extensions.m, .mm
Compiler entry pointsObjCCompiler.Compile() / ObjCCompiler.CompileFile()
Source filesLexer.cs (145 lines) / Parser.cs (440 lines) / CodeGenerator*.cs (399 lines) / ASTNode.cs (76 lines)

1. Overview

This compiler implements a subset of Objective-C 2.0, compatible with C syntax and extended with object-oriented features. The core design idea is to compile C functions and ObjC message sends uniformly into VML IR, with a runtime library providing the message dispatch skeleton for base classes such as NSObject.

Architecture layers:

Objective-C source (.m, .mm)
    → Lexer (lexical analysis) → token stream
    → Parser (syntax analysis) → AST
    → CodeGenerator (code generation) → VMLProgram (VML IR)
    → VMLAssembler / VMLRuntime → execution

Compilation pipeline:

  1. Preprocessing (Preprocessor) — expands #import/#include/#define and the predefined macros
  2. Lexical analysis (Lexer) — splits the source into a token stream, recognizing the ObjC-specific @ keywords and C operators
  3. Syntax analysis (Parser) — recursive-descent parsing, building the AST node tree
  4. Code generation (CodeGenerator) — translates the AST into a VML instruction sequence

2. Supported features

2.1 C-compatible types

The basic C type system is supported. Type keywords are decided centrally by Lexer.IsTypeName().

Type keywordDescriptionExample
int32-bit signed integerint a = 10;
float32-bit floating pointfloat f = 3.14;
double64-bit floating pointdouble d = 2.718;
char8-bit characterchar c = 'A';
voidno return value / empty typevoid func() { }
short16-bit integershort s = 100;
long32-bit integer (long long supports 64-bit)long l = 99999;
signedsigned modifiersigned int x;
unsignedunsigned modifierunsigned int u;
structstruct declaration (supported by the lexer; the parser can recognize struct as a type name, but parsing of the semantics of struct member access ./-> is not yet fully implemented)struct Point p;
enumenum declaration (recognized by the lexer as a keyword and treated by the parser as a type name, but enum member definitions are not yet implemented)enum Color c;
idObjC generic object pointer, equivalent to void *id obj = nil;

Type modifier combination rules: unsigned / signed may be followed by a base type name (such as unsigned int); the parser concatenates the type string recursively.

2.2 ObjC keywords

All ObjC keywords carry a @ prefix; the lexer reads the letter sequence after the @ character and matches it.

2.2.1 Core definition keywords
KeywordPurposeParsing status
@interfacedeclare a class interface (instance variables + method declarations)Fully parsed — supports class name, superclass, instance variable block, method declaration list
@implementationimplement class methodsFully parsed — supports method body implementations
@endterminate an @interface / @implementation blockFully parsed
@protocolprotocol declaration (recognized by the lexer; the parser implements no parsing logic)token only
@classforward-declare a class namerecognized and skipped by the parser (no code generated, no semantic effect)
@propertyproperty declaration (recognized by the lexer; the parser implements no parsing logic)token only
@synthesizeproperty accessor synthesis (recognized by the lexer; the parser implements no parsing logic)token only
@dynamicproperty accessors provided at runtime (recognized by the lexer; the parser implements no parsing logic)token only
@selectorselector literal (recognized by the lexer; the parser implements no parsing logic)token only

@interface syntax:

@interface MyClass : SuperClass {
    // Instance variables (only type + name are supported; the pointer * syntax is not)
    int count;
    float value;
    id delegate;
}
// Method declarations
- (void)doSomething;
- (int)add:(int)a to:(int)b;
@end

@implementation syntax:

@implementation MyClass

// Method implementations
- (void)doSomething {
    count = count + 1;
    return;
}

// C functions may also be mixed into @implementation
int helperFunc(int x) {
    return x * 2;
}

@end
2.2.2 Message expressions

The classic Objective-C square-bracket message send syntax is supported:

// Basic message send
[receiver methodName]

// Message send with parameters (parameters interleaved with selector labels)
[obj setX:10 y:20]

// Message with no parameters
[array count]

// Nested call (the receiver may be the result of another message expression)
[[self delegate] update]

Code generation: a message send compiles to a CALL objc_<method> instruction; the parameters and the receiver are PUSHed onto the stack in order. The runtime must provide a function named objc_<selectorName> to perform message dispatch.

2.2.3 NSString literals

The @"string" syntax for creating an ObjC string constant is supported:

id greeting = @"Hello, World!";

Internal implementation: the lexer recognizes @"..." as an ObjCString token (value = @"...content..."); the code generator treats it as a string literal, stores it in the data section and loads its address with MOVE R0, label (LEA semantics).

2.2.4 Special values
ValueTypeCompiles to
nilnull object pointerMOVE R0, 0
YESboolean trueMOVE R0, 1
NOboolean falseMOVE R0, 0
selfcurrent object reference (implicit parameter inside a method)stack frame offset 0 (R12 base)
supersuperclass reference (recognized by the lexer, semantics not implemented)token only
2.3 Method declarations

Both instance methods (-) and class methods (+) are supported:

// Instance method — no parameters
- (int)getCount {
    return count;
}

// Instance method — one parameter
- (void)setCount:(int)newCount {
    count = newCount;
}

// Instance method — several parameters (interleaved selector labels)
- (int)add:(int)a to:(int)b {
    return a + b;
}

// Class method (+)
+ (id)sharedInstance {
    return self;
}

Method name encoding: colons in the selector are replaced with underscores. For example add:to: compiles to the label objc_add_to_.

2.4 Operators
2.4.1 Fully supported operators (implemented in both the parser and the code generator)
CategoryOperatorsDescription
Arithmetic+ - * / %add, subtract, multiply, divide, modulo
Comparison== != < > <= >=equal / not equal / ordering comparisons
Assignment= += -= *= /= %=assignment and compound assignment
Unary- ! ++ --negation / logical not / increment / decrement
Dereference*pointer dereference (supported by the lexer + parser; code generation only performs a register-indirect load)
Address-of&address-of (supported by the lexer + parser; code generation only performs a register-indirect store)
2.4.2 Lexer only (the parser implements no semantics)
OperatorToken typeDescription
&& ||And, Orlogical and/or (short-circuit evaluation is not implemented)
& | ^ ~Amp, Pipe, Caret, Tildebitwise and/or/xor/not
<< >>LShift, RShiftshift left/right
->Arrowstruct pointer member access
? :Question, Colonternary conditional operator
...Ellipsisvarargs (recognized by the parser but has no semantics)
2.5 C function declarations
// Standard function declaration
int add(int a, int b) {
    return a + b;
}

// Function with no parameters
void sayHello() {
    return;
}

// Multi-parameter function
float average(float a, float b, float c) {
    return (a + b + c) / 3.0;
}

// Forward declaration (the function body is replaced by a semicolon)
int factorial(int n);

Calling convention: parameters are PUSHed onto the stack, called with CALL func_<name>, returned with RET, and the return value is in R0.

Function overloading: not supported. Function names must be unique across the whole program.

2.6 Control flow
2.6.1 if / else
if (x > 0) {
    return 1;
} else if (x == 0) {
    return 0;
} else {
    return -1;
}

Implementation: after the condition is evaluated, CMP R0, 0 + JE elseLabel; chained else if is supported.

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

Implementation: a loop-head label + a conditional JE to the exit + an unconditional JMP at the loop tail back to the loop head.

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

Implementation: the initializer runs outside the loop, the condition is evaluated before each iteration, and the update runs after the loop body.

2.6.4 Control flow not currently supported (the lexer already recognizes the keywords)

The following C control-flow keywords already have tokens defined in the lexer, but the parser implements no parsing logic for them:

2.7 Comments
// single-line comment

/* 
   multi-line comment
   may span several lines
*/

Both comment styles are discarded directly during lexical analysis and never enter the token stream.

2.8 Preprocessing

The compiler supports the #import, #include and #define preprocessing directives. A line starting with # is recognized by the lexer as a Hash token, and the parser calls SkipPreprocessor() to skip to the end of the line. The actual preprocessing logic runs at the ObjCCompiler.Compile() entry point, handled by the Preprocessor class:

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

3. Expression precedence

The function call chain of the recursive-descent parser defines the following precedence levels (from lowest to highest):

PrecedenceOperatorsAssociativityParse function
1 (lowest)= += -= *= /= %=rightParseAssignment()
2== != < > <= >=leftParseComparison()
3+ -leftParseAdditive()
4* / %leftParseMultiplicative()
5- ! * & ++ -- (prefix)rightParseUnary()
6 (highest)literals, variables, function calls, message sends, parentheses—ParsePrimary()

Notes:


4. Predefined macros

The compiler defines the following macros automatically at startup (see ObjCCompiler.PredefinedMacros):

MacroValueDescription
__VML__1marks the VML compilation environment
__VML_VERSION__"1.65.32"VML toolchain version number
__OBJC__1marks the Objective-C compiler
__DATE__"Jun 02 2026" (date at compile time)compilation date (MMM dd yyyy format)
__TIME__"15:30:45" (time at compile time)compilation time (HH:mm:ss format)

These macros are injected as the PredefinedMacros dictionary when they are handed to the Preprocessor, and take effect before the #defines in the source.


5. AST node types

Node classPurposeKey fields
ProgramNodetop-level program root nodeStatements: List<ASTNode>
FuncDeclNodeC function declaration/definitionName, ReturnType, Parameters, Body
VarDeclNodevariable declarationType, Name, Init
ObjCInterfaceNode@interface declarationName, SuperClass, Members
ObjCImplNode@implementation definitionName, Methods
ObjCMethodNodeObjC methodName (includes colons), ReturnType, Parameters, Body
ReturnNodereturn statementValue (may be null)
IfNodeif / else statementCondition, ThenBody, ElseBody
WhileNodewhile loopCondition, Body
ForNodefor loopInit, Condition, Update, Body
LiteralNodeliteralValue (int / float / string)
VarNodevariable referenceName
AssignNodesimple assignment =Name, Value
BinaryNodebinary expressionLeft, Op, Right
UnaryNodeunary expressionOp, Operand
CallNodeC function callName, Arguments
MsgSendNodeObjC message sendReceiver, Method (includes colons), Arguments

6. Code generation details

6.1 Register conventions

Same as the unified VML internal standard:

RegisterPurpose
R0accumulator / function return value / first argument
R1-R3function arguments
R4-R11general-purpose registers (caller-saved)
R12frame pointer (BP) (callee-saved)
R13stack pointer (SP)
R14link register (LR) (callee-saved)
R15return address (RA) (managed automatically by CALL/RET)
F0-F15single-precision floating-point registers (caller-saved)
D0-D7double-precision floating-point registers (caller-saved)
L0-L764-bit long integer registers (caller-saved)
6.2 Function/method naming rules
KindLabel prefixExample
C functionfunc_int main() → func_main
ObjC methodobjc_- (void)doIt:(int)x → objc_doIt_

A JMP skip_<label> instruction is generated before the function body to skip over it (so that execution does not fall into it); the function body ends with RET.

6.3 Variable storage
6.4 Message send code generation
[obj setX:10 y:20]

compiles to the following VML instruction sequence:

; push the arguments (no need to reverse them first; all arguments are PUSHed in order)
MOVE R0, 10
PUSH R0
MOVE R0, 20
PUSH R0
; push the receiver
MOVE R0, [R12-{obj_offset}]
PUSH R0
; call the method
CALL objc_setX_y_
6.5 self handling

Inside an ObjC method body, self is added to the symbol table automatically with offset 0 (that is, the top of the stack frame that the frame pointer R12 points to). The method body may use self like an ordinary variable reference.

6.6 Standard library linking

When compiling a file (CompileFile), the compiler automatically:

  1. Extracts the dependency library names from the #import / #include directives
  2. Resolves library files under the Lib/ directory by the objc language type
  3. Calls LibraryLinker to link the external symbols

The extractImports regular expression matches both #import and #include, and supports both angle brackets <> and double quotes "".


7. Known limitations

LimitationDetails
@property synthesisThe lexer recognizes @property/@synthesize/@dynamic, but the parser implements no automatic accessor synthesis at all. Properties must be written by hand as getter/setter methods
@protocolThe lexer recognizes the @protocol token; the parser implements no protocol declaration, adoption or conformance checking. The type system does not support the id<Protocol> syntax
CategoryNot implemented at all. The @interface ClassName (CategoryName) extension syntax is not supported
Block / ClosureNot implemented at all. The ^ block syntax and closure capture are not supported
ARC memory managementNo automatic reference counting, and no ownership qualifiers such as strong/weak/unsafe_unretained. Object lifetime must be managed by hand
Property attributesThe property attribute syntax @property (nonatomic, strong) is not parsed at all
Message forwardingforwardInvocation: / methodSignatureForSelector: are not implemented
KVC/KVOKey-value coding and key-value observing are not implemented
Exception handling@try @catch @finally @throw are not implemented at all (skipped in MCU mode)
struct/enum/union definitionsThe lexer recognizes the keywords, but parsing and code generation for struct member access ./-> are not implemented
Logical operators && / ||Tokens are defined, but they are not part of the expression parsing chain, so short-circuit logic cannot be used
Bitwise operatorsThe tokens & | ^ ~ << >> are defined, but the expression parser only supports & and * as unary address-of/dereference; binary bitwise operations are not supported
Ternary operatorThe ? : token is defined, but the parser implements no conditional expression
switch/caseTokens are defined, but the parser implements no multi-way selection
break/continueTokens are defined, but the parser implements no loop control
do-whileToken is defined, but the parser does not implement it
goto / labelsToken is defined, but the parser does not implement it
Type castingThe C-style explicit cast (type)expr is not supported
Arrays / pointer arithmeticArray subscripting [] and pointer arithmetic are not supported
Object allocationNo compile-time support for the alloc/init pattern or +new; the runtime must provide it by hand
super semanticsThe super keyword is a token only; there is no method-lookup semantics
Multi-file compilationEach call compiles a single .m file; class hierarchy resolution across files at link time is not supported

8. Grammar reference (EBNF summary)

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 ")"

9. Compilation command examples

# Compile a single .m file
dotnet run --project VMLTool -- input.m -o output.vml

# Compile a .mm file
dotnet run --project VMLTool -- app.mm -o app.vml

# Compile directly with ObjCCompiler (programmatic interface)
ObjCCompiler.Compile("int main() { return 0; }")
ObjCCompiler.CompileFile("MyClass.m", includePaths, libraryPaths)

10. Version history

VersionDateChanges
1.02025-12Initial implementation — lexer + recursive-descent parser + VML code generation
1.65.17+2026-05Integrated Preprocessor, the __OBJC__ predefined macro, and ExtractImports linking support

🆕 String encoding (v1.65.19)

This language compiler uses the VML string system indirectly through the shared library (Lib/shared/).

Pseudo-opWidthEncodingC type
.string8-bitUTF-8char*
.wstring16-bitUTF-16LEwchar_t*
.ustring32-bitUTF-32LEchar32_t*

MCU mode (default): strings are output as UTF-8 (.string) OS mode: the encoding can be tested with the VML_WSTRING macro

The shared library already provides wide-string conversion functions (wchar.h/uchar.h); each language compiler may use them as needed.

Compiler README

Objective-C Compiler

Path: VMLPrepares/ObjCCompiler/ Completeness: ~99% | 🟢 Production ready Standard library: Lib/objc/ Depends on: CCompiler (ObjC is a superset of C)

Features

Compilation modes

MCU mode (default --mode mcu)

ObjC assembles to the C-compatible subset retained for the target.

Skipped:

Retained:

OS mode (--mode os)

OS mode supports the full set of language features.

RAM levels

Usage

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

Tests