7个版本

0.2.5 2023年9月4日
0.2.4 2023年9月4日
0.1.2 2023年8月31日

#131 in 编程语言

每月42次下载

MIT许可协议

43KB
1.5K SLoC

Crates.io

fomoscript

小型脚本语言,使用Rust构建

  • 0依赖项*
  • 1 文件
  • no_std且支持alloc

仅几天前。 不是生产就绪。一个目标是将其用于Fomos作为shell。但你可以在任何地方部署它。

* 除了日志,不计入 ;)

Fomos内部演示

https://github.com/Ruddle/fomoscript/assets/14235713/a69e00cd-1985-4a54-9f75-e1e91a19949a

示例

简单脚本

{
    let x = 0
    while x<5 {
        x = x+1
    }
    x
}

返回5

支持高阶函数

{
    let x = 0
    let f = (e) => {e+1}
    let g = (f,e) => f(e)
    g(f,x)
}

返回1

用法

将此添加到您的Cargo.toml中

[dependencies]
fomoscript = "0.2.4"

解析和评估脚本

let result = fomoscript::parse_eval("{
    let x = 0
    while x<5 {
        x = x+1
    }
    x
}");

结果将具有此类型

查看测试以获取更多示例。

parse_eval是一个高级函数,隐藏了低级别的Ctx

您可以显式实例化一个名为Ctx的解释器来实现REPL或探索/修改执行过程中的状态。

默认情况下,在评估期间脚本中不可能有副作用(除了在ctx内部)

您可以将带有(或没有)副作用的本地Rust闭包插入到Ctx中,并在脚本内部使用它。例如,使用打印函数的示例

use fomoscript::*;
let code = r#"
{
    my_print(1+1)
}
"#;

let mut ctx = Ctx::new();
ctx.insert_code(code);

let print_closure = Rc::new(|a: N, _, _, _| {
    println!("{}", a.to_str());
    N::Unit
});
ctx.set_val("my_print", N::FuncNativeDef(Native(print_closure)));

let expr = ctx.parse_next_expr().unwrap();
let _ = eval(&expr, &mut ctx);

REPL

使用此代码片段构建自己的REPL。为了简单起见,这里使用了std,但您可以将其替换为任何输入和输出实现。

use fomoscript::*;
let mut ctx = Ctx::new();
let mut buffer = String::new();
loop {
    buffer.clear();
    std::io::stdin().read_line(&mut buffer).unwrap();
    ctx.insert_code(&buffer);
    while let Ok(parent) = ctx.parse_next_expr() {
        let res = eval(&parent, &mut ctx);
        println!("> {:?}", res);
    }
}

缺少的残酷事实

  • 标准库
  • JavaScript风格的对象
  • 错误处理
  • 引用字符串中的转义字符
  • 数月的工作
  • 模式匹配

内部工作方式也不太像Rust,但没有使用unsafe。在评估期间应该没有panic。请暂时不要相信解析器。

特性

  • 字符串类型
  • 数字类型(f64)
  • 作用域变量赋值
  • 二元运算符 +,-,/,*,>,<,==,!=,&,|
  • 运算符优先级
  • 高阶函数
  • 控制流 if/else/while
  • 自定义本地函数
  • 匿名函数调用$
  • REPL示例
  • 数组

性能

解析瞬间完成(50+GB/sec)。

评估较慢,但对于脚本来说合理

  • 与本地相比,最坏情况是1:1000
  • 常见情况1:20,在合理使用本地函数时。

cargo bench 来亲自看看。

非结构化数值计算将保持缓慢。未来可能会添加类型化数组(如js中的),以实现快速结构化操作。

有趣的事实

在fomoscript中,一切都是表达式。例如,if/else 起到三元运算符的作用。

letx= if 1 99 else 45

现在 x 是 99

当有疑问时,解释器默认为 N::Unit。例如,我们不要添加 else 分支

letx= if 0 1

x 现在是 N::Unit

条件或主体不需要括号,上一个表达式等价于

let x = if 0 {
    1
} else {
    N::Unit
}

同样的,while 也适用,如果它从未执行主体,则返回 N::Unit,或者如果至少执行了一次主体,则返回最后一个主体表达式。

同样适用于括号

let x = {1 2 3} 等价于 let x = {3}let x = 3

let x = {
    1
    2
    3
}

没有括号,使用括号 强制因式分解、优先级,并消除任何 歧义

\n 只是一个空格,它不像大多数语言那样在语句之间分隔。

布尔运算自动将操作数转换为布尔值(查看 to_bool 了解详情)

(and,or) 运算符是 (&,|)

1 & 0 的值为 0

1 | 0 的值为 1

目前还没有位运算。

数组

连接

[1,2,3] ++ [4,5,6]

返回 [1,2,3,4,5,6]

推入

[1,2,3] + 4

返回 [1,2,3,4]

[1,2,3] + [4,5,6]

返回 [1,2,3,[4,5,6]]

添加到前面

4 + [1,2,3]

返回 [4,1,2,3]

获取

[1,2,3](1)

返回 2

以逆序获取

[1,2,3](-1)

返回 3

映射

[1,2,3]((e) => e*2)

返回 [2,4,6]

第二个参数是元素的索引

[1,2,3]((e,i) => i)

返回 [0,1,2]

过滤

[1,2,3] & (e)=> e<3

返回 [1,2]

[1,2,3] & (e,i)=> i!=1

返回 [1,3]

归约

[1,2,3,4] | (a,b)=> a+b

返回 10

["hello","beautiful", "world"] | (a,b) => a+" "+b

返回 "hello beautiful world"

长度

[0,1,2]()

返回 3

依赖项

~87KB