1个不稳定版本
0.1.0 | 2022年3月20日 |
---|
#398 in 编程语言
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类似,语法非常简单。实际上只有字符串、数字、标识符、函数调用和引号列表。
基本语法如下
- 数字字面量:
10
,20
- 字符串字面量:
"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>)
:执行系统命令。