galah

一种脚本语言,目标是轻量级并可嵌入 Swift 应用程序。

尝试一下

在线 Playground

Galah playground 使用 WASM 在您的浏览器中运行 Galah 解释器!在现代浏览器中访问它,无需在本地安装即可试用 Galah。

本地构建

以下命令从源代码构建解释器的 CLI,并运行此存储库中包含的 sample.galah 文件,

git clone https://github.com/stackotter/galah
cd galah
swift run galah ./sample.galah

语法(WIP,大部分尚未实现)

此语法都可能会更改!一切都尚未确定,甚至语言的风格也尚未完全决定。

以下示例程序是强类型的,但严重依赖类型推断而不是类型注解。仍然不确定省略返回类型注解是否是个好主意。如果 LSP 支持足够好,应该没问题,但是当使用用户提供的内置函数等时,LSP 并不总是那么有用(LSP 在没有某种接口文件的情况下可以知道多少是有限制的)。

struct Citizen {
    let name: Str
    let age: Int

    // `name` is inferred to be of type `Str`.
    init(name) {
        self.name = name
        self.age = 0
    }

    fn randomName() -> Str {
        ["Steve Apple", "John Smith"].randomChoice()
    }

    // Return type is inferred as `(Str, Optional<Str>)`
    fn parsedName(self) {
        let parts = self.name.split(" ", maxSplits: 1)
        return (parts[0], parts.last)
    }

    // We could infer that it throws for the user, but that might not be a good idea. Note that
    // this method definitely shouldn't throw in any sane software, this is just an example.
    fn incrementAge(mut self) throws {
        self.age += 1
        if self.age > 100 || Int.random(0, 80) == 0 {
            throw "Citizen died"
        }
    }
}

fn main() {
    let stackotter = Citizen("stackotter")
    for _ in 0..<80 {
        do {
            try stackotter.incrementAge()
        } catch {
            eprint("stackotter died at {stackotter.age}")
            exit(1)
        }
    }

    print("stackotter is {stackotter.age}")
    let (first, last) = stackotter.parsedName()
    print("stackotter's first name is {first} and his last name is \(last ?? "unknown")")
}

这是相同的程序,但用 Python 编写以作比较,

from typing import Optional

class Citizen:
    name: Str
    age: Int

    def init(self, name: str, age: int):
        self.name = name
        self.age = age

    @classmethod
    def randomName(cls) -> str {
        return ["Steve Apple", "John Smith"].randomChoice()

    def parsedName(self) -> (str, Optional[str]):
        let parts = self.name.split(" ", 1)
        return (parts[0], parts[1] if len(parts) == 2 else None)

    def incrementAge(self):
        self.age += 1
        if self.age > 100 or random.randrange(0, 80) == 0:
            raise Exception("Citizen died")

if __name__ == "__main__":
    stackotter = Citizen("stackotter")
    for _ in range(80):
        try:
            try stackotter.incrementAge()
        catch Exception:
            print("stackotter died at {stackotter.age}")
            exit(1)

    print(f"stackotter is {stackotter.age}")
    first, last = stackotter.parsedName()
    print(f"stackotter's first name is {first} and his last name is \(last ?? 'unknown')")