7个版本
0.2.5 | 2023年9月4日 |
---|---|
0.2.4 | 2023年9月4日 |
0.1.2 | 2023年8月31日 |
#131 in 编程语言
每月42次下载
43KB
1.5K SLoC
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