【far 语言】语法总览

本文是「far 语言」系列文档之一,总目录见《【far 语言】语言文档目录》。

Far 语法速览

Far 语法融合了 JS 的灵动、Java 的严谨与 C 的直白。本文面向初识 far 的开发者,用最短篇幅带你读懂它。

你好,far

class App {
    static long main(string args[]){
        println("你好,far!");
        return 0;
    }
}

每个 .far 文件就是一个类,文件名即类名。入口固定为 static long main(string args[]),方法默认返回 0,可不写 return

变量与类型

string name = "far";                    // 字符串
long age = 30;                          // 64 位整数(默认数值类型)
int score = 100;                        // 32 位整数注解
double pi = 3.14;                       // 双精度浮点
bool ok = true;                         // 布尔
byte b = 0x48;                          // 8 位整数注解
x := 1;                              // 短变量声明 :=(自动推断类型,Go 风格)
map<string, string> data = {"name": "far"};  // 字典
object obj = new App();                // 对象 / 自定义类实例
  • 类型写在变量名前;短变量声明 x := 表达式 自动推断。
  • int/short/byte 是编译期宽度注解,运行时统一 64 位。
  • map 键可以是 stringlong,字面量用 {...}key 可以省略引号{name: "far", if: 123}

数组

数组采用 C 风格声明,方括号挂在变量名后,字面量用花括号:

int data[3] = [1,2,3];              // 定长数组(数量必须严格相等)
string names[] = ["a","b","c"];     // 未定长(动态)
int m[2][3] = [[1,2,3],[4,5,6]];    // 多维数组
object x[] = [1, 2.1, "3"];         // 混合类型(元素统一 far 对象)
bb := ["4","5"];                 // := 推导(元素类型一致)

names.push("d");        // 追加
names.pop();            // 弹出末尾
names[0] = "x";         // 下标读写
println(sizeof(names)); // 长度

方法

class App {
    // 静态方法,默认返回 0,可不写 return
    static long sum(long a, long b){
        return a + b;
    }

    // 参数默认值(带默认值的参数必须放在最后)
    static long greet(string name, long times = 1){
        for(long i = 0; i < times; i++){
            println("你好," + name);
        }
        return 0;
    }

    // 数组参数与返回
    static long first(int x[3]){ return x[0]; }
    static int[] getList(){ return [1,2,3]; }

    static long main(string args[]){
        long r = this::sum(3, 4);    // 静态方法用 :: 调用
        println(r);
        this::greet("far");          // 缺省参数
        this::greet("far", 3);
        return 0;
    }
}
  • 匿名函数:f := (x,y)-> println(x + y);,可捕获外部变量。
  • 参数解构(传 map 或数组均可),适合"具名可选参数"场景:
static long test(string type, {string name, long age}){
    println(fmt("%s %s %s", type, name, age));
}

// 调用:传 map 或数组都行
this::test("大学生", {"name":"张三", "age":21});
this::test("大学生", {"张三", 21});
  • 真·变长参数用 T... name(只能放最后),见下节与 function.md

类、接口与继承

一个 .far 文件内可声明多个类与接口;main 所在类为入口。

interface Named {
    string name();
}

class Animal {
    long hp = 100;
    string speak(){ return "animal"; }
}

// 单继承用冒号;多接口用 impl 逗号列表
class Dog : Animal impl Named {
    @Override
    string speak(){ return super.speak() + "+woof"; }
    string name(){ return "dog"; }
}

class App {
    static long main(string args[]){
        Named n = new Dog();        // 接口变量 ⇒ 动态分派
        println(n.name());          // dog
        Animal a = new Dog();       // 向上转型
        println(a.speak());         // animal+woof(按运行期类型分派)
        return 0;
    }
}
  • 只允许单继承(多继承会报错);需要多个能力请写 impl I1, I2
  • super.method() 调父类实现;@Override 断言真覆盖,签名写错立刻报错。
  • 父类字段初始化器自动执行;子类字段不得与父类同名(编译报错)。
  • 接口之间可继承(接口无状态,可多个):interface IChild : IParentA, IParentB { }
  • 详见 inherit.mdinterface-generic.md

流程控制

// if / else
if (a == 1) {
    b = 2;
} else if (a == 2) {
    b = 3;
} else {
    b = 4;
}

// for(初始器 / 条件 / 执行器 都可省略)
for(long i = 0; i < 10; i++){
    println(i);
}

// foreach 遍历 map / 数组
foreach(k in data){ println(data[k]); }   // k 是 key
foreach(v in arr){ println(v); }          // v 是元素
foreach(idx, v in arr){ println(idx); }   // idx 是索引

// when 表达式(比 switch 更强,带返回值)
r := when{
    a == 2 => "等于2";
    a in (1,2,3) => "在集合里";
    a in 1..20 => "在区间里";
    _ => "默认";
};

// 区间表达式
if (x in 1..10){ println("x 在 1..10"); }

并发:虚拟线程 + Channel

Far 的所有异步任务统一为虚拟线程(vtNew),通信只走 Channel,select 多路等待。

class App {
    static long main(string args[]){
        // 创建通道对(tx 发送端 / rx 接收端)
        map<string, object> ch = chanPair();
        map<string, object> tx = ch.tx;
        map<string, object> rx = ch.rx;

        // 创建并启动虚拟线程(匿名方法闭包,端点由闭包捕获)
        map<string, object> t = vtNew((dummy)->{
            string msg = chanRecv(rx);
            chanSend(tx, "got: " + msg);
        }, 0, 0, "worker");
        vtStart(t);

        // 主协程发送并阻塞接收
        chanSend(tx, "hello");
        string r = chanRecv(rx);
        println(r);
        return 0;
    }
}

select 多路等待多个 Channel:

select {
    case msg = cmd_rx.recv() => {
        if (msg == "quit") { break; }
    }
    case data = data_rx.recv() => { ... }
    timeout(100) => { }         // 超时分支
}

字符串

s := "Hello Far World";
println(s.len());                       // 15
println("  AbC  ".trim().toLowerCase()); // abc
println("a,b,c".split(",")[1]);         // b
println(s.charAt(1));                   // e

// 字符串模板
name := "far";
println(`Hello ${name}!`);              // Hello far!

集合:List

import List;                            // 导入标准库

List<long> nums = new List();
nums.add(1);
nums.add(2);
println(nums.size());                   // 2
println(nums.get(1));                   // 2
println(nums.toString());               // [1,2]

注解(Java 风格)

@Extern("c", "strlen")                  // 内建:FFI 声明外部 C 函数
static long strlen(RawPtr<AbiChar> s);

@Retention(RUNTIME)
@Target(CLASS, METHOD)
annotation MyAnno {
    string name;
}
  • 注解名首字母必须大写。
  • 默认只保留到编译期(SOURCE);@Retention(RUNTIME) 可让注解在运行期被反射读取。

原生内存:bytes + load/store

buf := bytes(64);                    // 分配 64 字节连续内存
bytesStore<i32>(buf, 0, 42);                 // 类型化写入
x := bytesLoad<i32>(buf, 0);              // 类型化读取
println(x);                             // 42

异常处理

try {
    throw "出错啦";
} catch (Exception e) {
    println(e.getMessage());
} finally {
    println("无论是否异常都会执行");
}

完整语法参考:grammar/all.md(由编译器源码核对);并发架构见 concurrency-final.md

posted @ 2026-09-13 05:05  方东信  阅读(4)  评论(0)    收藏  举报