1个不稳定版本

0.1.0 2022年3月20日

#398 in 编程语言

MIT许可证

48KB
1.5K SLoC

Osyris

Osyris是一个无依赖的纯Rust编程语言。它是一种LISP,旨在易于嵌入。它的执行速度不是非常快,但是解析速度非常快,代码几乎立即开始执行。

API

当您将Osyris用作库时,需要了解的主要概念是Reader、Scope和eval函数。Reader将输入文件解析为表达式,Scope是从名称到变量的映射,而eval函数接受一个表达式和一个Scope并产生一个值。

以下是一个简单的示例程序

use osyris::{eval, parse, stdlib};
use std::cell::RefCell;
use std::rc::Rc;

// The code we want to execute
static CODE: &'static str = r#"
(def 'fib {bind args 'num
     {if (<= num 1)
         1
         {+ (fib (- num 1))
            (fib (- num 2))}}})

(print "fib of 20 is" (fib 20))
"#;

fn main() -> Result<(), String> {
    // Create a reader which will give us expressions
    let mut reader = parse::Reader::new(CODE.as_bytes());

    // Create a scope, and populate it with the stdlib
    let scope = Rc::new(RefCell::new(eval::Scope::new()));
    stdlib::init(&scope);

    // Read and eval expressions
    loop {
        // We get a ParseError if the syntax is wrong
        let expr = match parse::parse(&mut reader) {
            Ok(expr) => expr,
            Err(err) => {
                println!("Parse error: {}:{}: {}", err.line, err.col, err.msg);
                break;
            }
        };

        // If the returned expression is None, we reached EOF
        let expr = match expr {
            Some(expr) => expr,
            None => break,
        };

        // Evaluate the expression
        match eval::eval(&expr, &scope) {
            Ok(_value) => (), // Ignore the return value
            Err(err) => {
                println!("Eval error: {}", err);
                break;
            }
        };
    }

    Ok(())
}

语法

与大多数LISP类似,语法非常简单。实际上只有字符串、数字、标识符、函数调用和引号列表。

基本语法如下

  • 数字字面量:1020
  • 字符串字面量:"hello world""with \"escapes\""'identifier-strings
  • 标识符:look-like-this

函数调用是一个开括号,后面跟着表达式,然后是一个闭括号,如下所示:(print 10 "hello" name)。期望第一个表达式的值是一个引号列表(或内置函数)。

引号列表类似于函数调用,但前面有一个引号:'(print 10 "hello" name)。当计算时,引号列表变成表达式列表。函数通常接受一个引号列表作为参数作为回调函数。因为它们非常常见,Osyris允许您用大括号来编写它们:{print 10 "hello" name}

示例

if 是一个函数,它接受一个条件和一到两个引号

(if (> 10 20)
    {print "10 is greater than 20"}
    {print "10 is not greater than 20"})

def 用于在当前作用域中引入一个值

(def 'age 32)
(print "Your age is" age)

您可以通过定义其体为引号的变量来定义函数

(def 'say-hello {print "Hello!"})
(say-hello)

函数会隐含获得一个 args 值,它包含函数参数

(def 'say-hello {print "Hello," (args 0)})
(say-hello "Bob")

您可以使用 bind 函数为参数命名

(def 'say-hello {bind 'name 'age {print "Hello," age "year old" name}})
(say-hello "Annie" 41)

标准库

当您调用 stdlib::init 时,这些值将被填充

  • none:None 类型的变量。
  • (print [values...]):一个将内容打印到标准输出的函数。
  • (+ [numbers...]):将数字相加。
  • (- [head] [tail...]):从头部减去尾部中的数字。
  • (* [numbers...]):将数字相乘。
  • (/ [head] [tail...]):将头部数字除以尾部中的数字。
  • (== [values...]):如果所有值都相等,则返回 1,否则返回 0。
  • (!= [values...])== 的相反数。
  • (<= [values...]):如果每个值都不大于下一个值,则返回 1,否则返回 0。
  • (< [values...]):如果每个值都小于下一个值,则返回 1,否则返回 0。
  • (>= [values...]):如果每个值都不小于下一个值,则返回 1,否则返回 0。
  • (> [values...]):如果每个值都大于下一个值,则返回 1,否则返回 0。
  • (|| [values...]):返回第一个真值,如果所有都是假值,则返回最后一个值。
  • (&& [values...]):返回第一个假值,如果所有值都是真值,则返回最后一个值。
  • (def <name> <value>):在当前作用域中创建一个名为 name 的新变量。
  • (set <name> <value>):替换名为 name 的现有变量。
  • (if <condition> <body> [else-body]):如果 condition 是真值,则执行 body,否则如果存在,则执行 else-body
  • (match [pairs...] [default]):根据谓词执行一组选项中的一个。示例
(def x 55)
(match {> x 10} {print "It's greater than 10"}
       {< x 10} {print "It's smaller than 10"}
       {print "It's neither greater nor smaller than 10"})
  • (while <condition> <body>):当 condition 执行为真值时,执行 body
  • (do [values...]):返回最后一个值。
  • (bind <array> [names...] <body>):将 array 中的值绑定到名称,然后执行 body
  • (with [pairs...] <body>):将值绑定到名称,然后执行 body。示例
(with 'x 10
      'y 20
      {print "x + y is" (+ x y)})
  • (list [values...]):创建一个列表。
  • (dict [pairs...]):创建一个字典。示例
(dict 'name "Bob"
      'age 34
      'profession "Programmer")
  • (lazy <quoted list>):创建一个懒变量。
  • (read <port> [size]):从 port 读取。如果提供了 size,则读取一个块。
  • (write <port> <data>):将 data 写入 port
  • (seek <端口号> <偏移量> [基准点]):将指针移动到偏移量。默认情况下,偏移量相对于起始位置,但基准点可以是以下之一:'set'end'current来改变这一点。

IO 库

这些值在你调用iolib::init时被设置。

  • (open <路径>):以只读模式打开路径处的文件。
  • (create <路径>):在路径处创建或截断文件,以只写模式打开。
  • (exec <argv>):执行系统命令。

无运行时依赖