#编程语言 #高级 #转译 #实现 #语法 #伴侣 #高级

galvan

一种将代码转译为 Rust 的高级编程语言

9 个版本

0.0.0-dev092024年1月20日
0.0.0-dev082024年1月11日
0.0.0-dev032023年12月26日
0.0.0-dev022023年11月30日

#285编程语言

Download history 4/week @ 2024-03-07 21/week @ 2024-03-14 30/week @ 2024-03-28 27/week @ 2024-04-04

96 每月下载量

MIT 许可证

21KB
155

Galvan

Rust 的高级伴侣语言。

[!重要] 这是一个正在进行中的项目,处于开发阶段。目前它处于一个业余项目状态,此处描述的大多数功能尚未实现。我在业余时间做这个项目 - 如果您喜欢这里提出的思想,并想帮忙,请随时联系我或在这里的 GitHub 上发起讨论。

动机

Galvan 不是什么

Galvan 并非旨在取代 Rust。它是一种转译为 Rust 的伴侣语言。这意味着,Galvan 的抽象不一定是无成本的,而是 Galvan 尝试为大多数用例选择一个合理的默认值。

Galvan 并不适用于低级编程 - 因此您不应使用它来构建解析器、编译器或音频压缩库。相反,Galvan 旨在用于高级应用程序,如 CLI 工具、Web 服务器等。最终目标是实现与 Rust 的完全互操作性,这样您就可以用 Galvan 编写应用程序,并依靠完整的 Rust 生态系统。

为什么选择 Galvan?

Rust 是一种非常好的语言,但它并非总是每个任务的最佳选择。Rust 的语法可能相当冗长,其类型系统和借用检查器 - 虽然非常强大 - 对于高级应用程序来说可能是一个负担,对于初学者来说可能令人不知所措。对于低级库和所谓的“系统编程”,Rust 在有用的抽象和掌握实现细节之间找到了一个完美的平衡点。然而,对于应用程序级编程来说,提供对常见用例的合理选择非常重要。这就是 Galvan 出现的地方:它提供了一种简洁的语法和简化 Rust 编写的方式 - 而不必担心生命周期、所有权等问题。

Galvan 之旅

Galvan 简介

Galvan 是一种将代码转译为 Rust 的现代编程语言。它提供了一种简洁的语法,同时利用 Rust 生态系统的全部功能。

基本语法和字符串格式化

在 Galvan 中,main 不是一个函数,而是一个“入口点”。

main {
    let name = "Galvan"
    print("Welcome to {name}, the modern language!")
}

请注意,Galvan 字符串始终支持内联格式参数。

函数

与 Rust 类似,函数使用 fn 关键字定义,并返回最后一个表达式的值

fn add(a: Int, b: Int) -> Int {
    a + b
}

非常短的函数也可以使用 = 定义,并自动推断其返回类型

[!警告] 使用 = 定义函数尚未实现

fn add(a: Int, b: Int) = a + b

这些函数的体内不允许有换行符。

类型

在Galvan中,类型是通过使用type关键字定义的。

/// A struct definition
pub type Color {
    r: Int
    g: Int
    b: Int
}

// Structs can also use the named tuple syntax
pub type Person(name: String, age: Int)

/// A type alias
pub type Human = Person

/// A tuple type
pub type Couple(Person, Person)

枚举可以具有关联值,可以是所有变体或特定变体。枚举也使用type关键字声明。

[!WARNING] 枚举尚未实现

/// An enum type
/// Enums can have general fields that are accessible to all enum variants
pub type Theme(name: String) {
    Plain
    /// Like in Rust, enum variants can have associated values, either named or unnamed
    Monochrome(Color)
    /// Unlike in Rust, '(' is also used for enum variants with named fields
    Dark(background: Color, foreground: Color)
    Light(background: Color, foreground: Color)
}

成员函数

所有函数都是声明为顶级。如果它们的第一参数命名为self,它们可以作为成员函数调用

pub type Dog(name: String)

fn bark(self: Dog) {
    print("{self.name} barks")
}

main {
    let dog = Dog(name: "Bello")
    dog.bark()
}

集合

Galvan为集合类型提供了语法糖。

pub type IntArray = [Int] // This is a Vec
pub type StringSet = {String} // This is a HashSet
pub type MyDict = {String: Int} // This is a HashMap
pub type OrderedDict = [String: Int] // This is an IndexMap

有序类型使用[],无序类型使用{}

可选类型和结果类型

Galvan为可选类型和结果类型提供了简洁的语法。

type OptionalInt = Int?
type FileOrErr = File!
type FileOrIoErr = File!IoError

错误变体指定在!符号之后。如果没有给出,则使用灵活的错误类型。

[!WARNING] !???尚未实现

fn open_file(path: String) -> File! {
    let file = File::open(path)!
    let contents = file.read_to_string()?.find("foo")?.uppercase() ?? ""
    
    contents
}

!运算符解包结果并在结果为错误时提前返回。这与Rust中的?运算符相同。

?是Galvan中的安全调用运算符。只有当结果不是错误也不是none时,后续表达式才会被评估。

??是空合并运算符,您可以使用它为左侧表达式是none时提供一个默认值。空合并运算符的右侧不能是返回或抛出表达式。

联合类型

Galvan支持在需要类型标识符的所有地方使用联合类型。

[!WARNING] 联合类型尚未实现

fn print_value(value: Int | String) {
    print("Value: {value}")
}

按值传递和按引用传递

可变参数与不可变函数参数

默认情况下,参数是按值传递的。如果参数需要被修改,可以使用mut关键字按引用传递:为了保持一致性,允许使用let关键字,但它冗余,因为默认情况下参数是按值传递的。

fn add_one(mut value: Int) {
    value += 1
}

// Using `let` is not necessary here but allowed
fn incremented(let value : Int) -> Int {
    value + 1
} 

Galvan的mut value: T与Rust的value: &mut T等效。Galvan没有不可变引用,因为所有值都是按需复制的。

// No copy is happening here as the value is not mutated
// Arguments are passed by value by default
fn bark_at(self: Dog, other: Dog) {
    print("{self.name} barks at {other.name}")
}
// A copy is happening here as the value is mutated
fn shout_at(self: Dog, other: Dog) {
    // Redeclaring is neccessary as value parameters cannot be mutated
    let other = other
    // Copy is happening here
    other.name = other.name.uppercase()
    print("{self.name} shouts at {other.name}")
}
fn grow(mut self: Dog) {
    // This mutates the original value as it is passed by reference
    self.age += 1
}

存储引用

可以存储在结构体中的引用必须声明为堆引用。这是通过在声明前加上ref来完成的。

pub type Person {
    name: String
    age: Int
    // This is a heap reference
    ref dog: Dog
}

main {
    // Note that constructors use '(' with named arguments
    ref dog = Dog(name: "Bello", age: 5)
    // The `dog` field now points to the same entity as the `dog` variable 
    let person = Person(name: "Jochen, age: 67, dog: ref dog)
    dog.age += 1
    
    print(person.dog.age) // 6
    print(dog.age) // 6
}

堆引用使用原子引用计数,在不再需要时自动释放,并且始终是可变的。与letmut值不同,ref值遵循引用语义,即它们指向同一个对象。因此,它们始终是可变的。

参数修饰符

在调用具有mutref参数的函数时,必须分别注释参数。对于成员函数的接收器不是这种情况。

fn make_uppercase(mut arg: String) { ... }

fn store(ref arg: String) { ... }

main {
    ref my_string = "This is a heap ref"
    
    // Argument must be annotated as mutable
    make_uppercase(mut my_string)
    // Argument must be annotated as ref
    store(ref my_string)
}

通过将参数注释为 mut,调用者承认在调用此函数时可能对给定参数进行原地修改。不可变变量或不可变结构体实例的成员(使用 let 声明)不能作为 mut 传递。

通过将参数注释为 ref,调用者承认函数可能存储一个可变(堆)引用。只有声明为 ref 的变量和成员可以作为 ref 传递。

控制流

循环

与 Rust 一样,循环可以产生一个值

[!WARNING] 循环尚未实现

mut i = 0
let j = loop {
    if i == 15 {
        return i
    }
    i += 1
}
print(j) // 15
print(i) // 15

也支持 for 循环

[!WARNING] for 循环尚未实现

for 0..<n {
    print(it)
}

循环变量可以通过 it 关键字访问,但也可以使用闭包参数语法显式命名

for 0..<n |i| {
    print(i)
}

注意,范围声明使用 ..<(排他上限)或 ..=(包含上限)。

如果-否则

[!WARNING] 嵌套 if-else 尚未实现

if condition {
    print("Condition is true")
} else if other_condition {
    print("Other condition is true")
} else {
    print("No condition is true")
}

尝试

您可以使用 try 解包结果或可选值

[!WARNING] 通过 it 的隐式参数尚未实现

try potential_error {
    print("Optional was {it}")
} else {
    print("Error occured: {it}")
}

解包变体可以通过 it 关键字访问,就像在闭包中一样。您也可以使用闭包参数语法命名它们以显式声明

try potential_error |value| {
    print("Optional was {value}")
} else |error| {
    print("Error occured: {error}")
}

if 一样,您也可以在没有 else 分支的情况下使用 try

try potential_error |value| {
    print("Optional was {value}")
}

let optional = try potential_error |successful| { successful }

这可以用于将解包的值传递给函数,或将 Result 转换为 Optional。

返回和抛出

返回值是隐式的,但是您可以使用 return 关键字提前返回

[!WARNING] return 关键字尚未实现

fn fib(n: Int) -> Int {
    if n <= 1 {
        return n
    }
    fib(n - 1) + fib(n - 2)
}

使用 throw 关键字提前返回错误

[!WARNING] throw 关键字尚未实现

fn checked_divide(a: Float, b: Float) -> Float! {
    if b == 0 {
        throw "Division by zero"
    }
    a / b
}

泛型

在 Galvan 中,类型标识符始终以大写字母开头。使用小写字母开头会引入类型参数

[!WARNING] 泛型尚未实现

type Container {
    value: t
}

fn get_value(self: Container<t>) -> t {
    self.value
}

可以使用 where 关键字指定边界

fn concat_hash(self: t, other: t) -> t where t: Hash {
    self.hash() ++ other.hash()
}

运算符

内置运算符

Galvan 提供了广泛的内置运算符。虽然它们都有 ASCII 变体,但 Galvan 在合理的情况下也接受 Unicode 符号。

算术运算符

  • +:加法
  • -:减法
  • *:乘法
  • /:除法
  • %:余数
  • ^:乘方

[!NOTE] Galvan 不提供与 对应的 Unicode 逻辑运算符,因为它们可能会与 v^ 分别混淆。如果您想使用 Unicode 运算符,您可以自己定义它们。逻辑运算符

  • and&&:逻辑与
  • or||:逻辑或
  • xor^^:逻辑异或
  • not!:逻辑非

[!警告] 位运算符尚未实现,但位运算符前缀为 b

  • b|:按位或
  • b&:按位与
  • b^:按位异或
  • b<<:按位左移
  • b>>:按位右移
  • b~:按位非

比较运算符

  • ==:相等
  • !=:不等
  • <:小于
  • <=:小于等于
  • >:大于
  • >=:大于等于
  • ===:指针相等,仅适用于堆引用
  • !==:指针不等,仅适用于堆引用

集合运算符

  • ++:连接
  • --:移除
  • []:索引
  • [:]:切片
  • in:成员资格

Unicode 和自定义运算符

Galvan 支持 Unicode 和自定义运算符

[!警告] 自定义运算符尚未实现

@infix("")
fn xor(lhs: n, rhs: n) = lhs ^^ rhs

@prefix("")
fn sqrt(n: Float) = n.sqrt()

main {
    let a_bool = true 
    let other_bool = false
    let value = if a_bool ⨁ other_bool {16.0 } else { 3.0 }
}

本节定义了自定义中缀 和前缀 运算符。请注意,前缀运算符和操作数之间不允许有空格。中缀运算符必须由空格包围。

闭包

闭包使用参数列表语法定义

let add = |a, b| a + b

闭包类型使用箭头语法

fn map(self: [t], f: t -> u) -> [u] {
    mut result = []
    for self {
        result.push(f(it))
    }
    result
}

尾随闭包

允许带有尾随闭包的函数省略参数列表和参数列表周围的括号

iter
    .map { it * 2 }
    // Trailing closures with only one parameter can use the it keyword instead of naming it explicitly
    .filter { it % 2 == 0 }
    // The parameter list before the trailing closure can be omitted
    .reduce start |acc, e| { acc + e }

尾随闭包也可以使用编号参数而不是提供参数列表

iter
    .map { #0 * 2 }
    .filter { #0 % 2 == 0 }
    .reduce start { #0 + #1 }

无括号函数调用

在语句或作为赋值的右侧,可以省略函数调用的括号

fn add(a: Int, b: Int) -> Int {
    a + b
}

main {
    let result = add 2, 3
    print result // 5
}

由于此语法仅在赋值和语句中允许使用,因此您不能在例如函数参数中使用它。这避免了歧义。也不允许使用此语法来表示不接受参数的函数,以避免与变量混淆。

测试

每个阻碍编写单元测试的因素都是一个尚未编写的单元测试。因此,Galvan 提供了一种简洁的语法,以便在任何 .galvan 文件中快速编写单元测试

test {
    assert 2 == 2
}

test "Ensure that addition works correctly" {
    assert 2 + 2 == 4
}

与 'main' 类似,'test' 不是一个函数,而是入口点。测试可以接受一个字符串作为描述。虽然这是可选的,但强烈建议在单元测试中添加简短描述。

依赖关系

~6–15MB
~180K SLoC