Skip to main content

swan_core/
lib.rs

1//! Swan is a small, dynamically typed, expression-oriented language designed to
2//! be embedded in template engines and other host applications. This crate is
3//! its reference implementation: a lexer, a parser, an evaluator, the runtime
4//! values and the built-in library. It has no dependencies.
5//!
6//! The language itself is described in the
7//! [Swan language manual](https://onlabsorg.github.io/texl/book/swan/index.html).
8//! This documentation covers the Rust API.
9//!
10//! # Quick start
11//!
12//! Source text goes through three stages: the [`Lexer`](lexer::Lexer) turns it
13//! into tokens, the [`Parser`](parser::Parser) turns the tokens into an
14//! [`ast::Node`], and [`evaluator::eval`] turns the node into a
15//! [`Value`](value::Value), using a [`Scope`](value::Scope) to look up names.
16//! [`builtins::root_scope`] creates a scope that already contains the built-in
17//! library.
18//!
19//! ```
20//! use swan_core::builtins::root_scope;
21//! use swan_core::evaluator::eval;
22//! use swan_core::lexer::Lexer;
23//! use swan_core::parser::Parser;
24//! use swan_core::value::Value;
25//!
26//! let scope = root_scope();
27//! let ast = Parser::new(Lexer::new("double = x -> x * 2, double 21")).parse();
28//! assert_eq!(eval(&ast, &scope), Value::Numb(42.0));
29//!
30//! // Assignments are stored in the scope, so a later evaluation can use them.
31//! let ast = Parser::new(Lexer::new("double 5")).parse();
32//! assert_eq!(eval(&ast, &scope), Value::Numb(10.0));
33//! ```
34//!
35//! # Design rules
36//!
37//! The whole crate follows a few rules. Knowing them makes the rest of the API
38//! easier to read.
39//!
40//! - **Evaluation never fails.** [`evaluator::eval`] never panics and never
41//!   returns an error. Syntax errors, type mismatches and invalid operations
42//!   all produce a [`Value::Undefined`](value::Value::Undefined) value, which
43//!   flows through the rest of the expression like any other value. Built-in
44//!   functions follow the same rule.
45//! - **Every value is a tuple.** `Nothing` is the empty tuple, a single value
46//!   is a one-item tuple, and [`Value::Tuple`](value::Value::Tuple) holds two
47//!   or more items. Tuples are always flat and never contain `Nothing`.
48//!   Operators apply item by item to tuples, except the short-circuit
49//!   operators `&`, `|`, `?` and `;`, which test their left operand as a
50//!   whole and evaluate the right one only when needed.
51//! - **Single-threaded and shared.** Values use `Rc`, so cloning is cheap and
52//!   nothing is `Send`. [`Scope`](value::Scope), [`Promise`](value::Promise)
53//!   and [`BuiltinFn`](value::BuiltinFn) are handles: clones share the same
54//!   state.
55//!
56//! # Extending the language
57//!
58//! A host adds its own functions by binding [`BuiltinFn`](value::BuiltinFn)
59//! values in a scope:
60//!
61//! ```
62//! use swan_core::builtins::root_scope;
63//! use swan_core::evaluator::eval;
64//! use swan_core::lexer::Lexer;
65//! use swan_core::parser::Parser;
66//! use swan_core::value::{BuiltinFn, Value};
67//!
68//! let scope = root_scope();
69//! scope.set("shout", Value::Builtin(BuiltinFn::new("shout", |arg| match arg {
70//!     Value::Text(t) => Value::Text(t.to_uppercase().into()),
71//!     other => Value::undef_unary("shout", other, 0),
72//! })));
73//!
74//! let ast = Parser::new(Lexer::new(r#"shout "hello""#)).parse();
75//! assert_eq!(eval(&ast, &scope), Value::Text("HELLO".into()));
76//! ```
77
78#![warn(missing_docs)]
79
80pub mod token;
81pub mod lexer;
82
83pub mod ast;
84pub mod parser;
85
86pub mod value;
87pub mod evaluator;
88
89pub mod builtins;