#language #lisp #extension #repl #interpreter

lep

适用于交互式控制台的迷你语言,具有小巧的代码占用

13个版本

0.5.0 2022年12月22日
0.4.5 2021年6月12日
0.4.4 2020年2月22日
0.4.3 2019年5月10日
0.1.0 2019年4月17日

#197 in 编程语言


2 个crate中使用(通过 gain-lep

MIT 协议

46KB
1K SLoC

Lep

适用于交互式控制台的迷你语言,具有小巧的代码占用。它本质上是一个糖包装的Lisp子集,类似于shell语法,受Python影响。

核心语言是函数/声明式的。它不是图灵完备的:不支持函数创建、循环和递归。支持顶层(会话作用域)的变量绑定。扩展函数可以引入副作用。

想法是Rust程序(嵌入lep)实现扩展函数,从而有效地创建一个领域特定查询或管理语言。宿主程序还负责设置REPL(读取-评估-打印循环),从而选择从哪里读取输入以及打印什么。

语法

评估是按行进行的:一行对应一个语句。一个语句产生一个可观察的结果值。

语句语法(两部分都是可选的)

!variable expression

变量名是任何非空白字符字符串,不包括 ()。表达式是正常的 s-expression,但最外层的括号可以省略。

语句返回表达式的值。如果存在 ! 前缀,则值也分配给变量。如果变量名为空,则选择一个未使用的名称(例如 $1)。如果省略 ! 前缀,则值分配给 _ 变量。如果省略表达式,则评估 _ 变量。

示例

>> echo "foo"
foo
>> + 1 2 3
6
>> (+ _ 10)
16
>> * (/ (- 10 5) 2) 50
100
>> ()
>> cons 1 2
(1 . 2)
>> list 1 2
(1 2)
>> ! + 1 2 3
$1 = 6
>> !x + 1 2 3
x = 6
>> !y (* x 2)
y = 12
>> and (or (and x y) (or)) true false
false
>> !z
z = false
>> !
$2 = false
>> env
(($2 . false) (z . false) (y . 12) (x . 6) ($1 . 6))

语句语法优化为两步使用

  1. 评估一个表达式
  2. 决定结果是否值得记住!

没有外部括号的表达式根据第一个术语的类型评估变量/字面量或调用函数。

当第一个术语是变量或字面量且存在多个术语时,表达式被引用。

>> 1 2 3
(1 2 3)

只包含空白的语句生成 () 并不改变变量。当用户输入空行时,嵌入程序无需评估它,可以执行其他操作。

函数

内置函数

(and arg1 arg2 ...)
(or arg1 arg2 ...)
(not arg)
(apply function arguments)
(+ arg1 arg2 ...)
(- arg1 arg2 ...)
(* arg1 arg2 ...)
(/ arg1 arg2 ...)
(car arg)
(cdr arg)
(cons arg1 arg2)
(list arg1 arg2 ...)
(drop list count)
(take list count)
(env)

自定义扩展函数的签名为 fn(args: &Rc<dyn Any>) -> Result<Rc<dyn Any>, String> 或实现 lep::Funlep::FunMut 特性。

类型

Lep 具有动态和开放的类型系统。类型 Rc<dyn Any>(别名 lep::Obj)用于在函数之间传递不可变值。扩展函数可以返回和接受自定义类型,但不能与内置算术函数一起使用,逻辑函数将它们的值视为真实值。

完全支持的类型

  • ()
  • bool
  • i64
  • String
  • lep::Name(符号)
  • lep::Ref(函数)
  • lep::Pair(一致性单元,例如列表节点)

以下值被视为不真实

  • ()
  • false
  • 0(仅 i64)
  • ""(仅 String)

评估

扩展函数绑定到一个 lep::Domain 对象

let mut domain = lep::Domain::new();
lep::builtin::register(&mut domain);
domain.register("nop", |args: &lep::Obj| Ok(lep::obj::nil()));

评估迭代从一个现有的 lep::State 开始,并用一个新的替换它

let mut state = lep::State::new();
loop {
    match lep::eval_stmt(&mut domain, state.clone(), read_line()) {
        Ok(new_state) => {
            // Print result here.
            state = new_state;
        }
        Err(msg) {
            println!("error: {}", msg);
        }
    }
}

无运行时依赖