diff --git a/Cargo.toml b/Cargo.toml
index 812c561..40cd8f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
name = "orchidlang"
version = "0.2.1"
edition = "2021"
-license = "AGPL-3.0-or-later"
+license = "GPL-3.0-or-later"
repository = "https://github.com/lbfalvy/orchid"
description = """
An embeddable pure functional scripting language
diff --git a/README.md b/README.md
index a298a8a..71eb825 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ Namespaces are inspired by Rust modules and ES6. Every file and directory is imp
The project uses the nighly rust toolchain. Go to one of the folders within `examples` and run
```sh
-cargo run
+cargo run --release
```
you can try modifying the examples, but error reporting for the time being is pretty terrible.
diff --git a/icon.svg b/icon.svg
new file mode 100644
index 0000000..3a50ba9
--- /dev/null
+++ b/icon.svg
@@ -0,0 +1,157 @@
+
+
+
+
diff --git a/src/foreign.rs b/src/foreign.rs
index 7eb5aaf..80529d0 100644
--- a/src/foreign.rs
+++ b/src/foreign.rs
@@ -18,8 +18,12 @@ use crate::representations::Primitive;
/// Information returned by [Atomic::run]. This mirrors
/// [crate::interpreter::Return] but with a clause instead of an Expr.
pub struct AtomicReturn {
+ /// The next form of the expression
pub clause: Clause,
+ /// Remaining gas
pub gas: Option,
+ /// Whether further normalization is possible by repeated calls to
+ /// [Atomic::run]
pub inert: bool,
}
impl AtomicReturn {
@@ -38,6 +42,7 @@ pub type XfnResult = Result;
/// Errors produced by external code
pub trait ExternError: Display {
+ /// Convert into trait object
fn into_extern(self) -> Rc
where
Self: 'static + Sized,
@@ -123,21 +128,27 @@ where
/// to pass control back to the interpreter.btop
pub struct Atom(pub Box);
impl Atom {
+ /// Wrap an [Atomic] in a type-erased box
pub fn new(data: T) -> Self {
Self(Box::new(data) as Box)
}
+ /// Get the contained data
pub fn data(&self) -> &dyn Atomic {
self.0.as_ref() as &dyn Atomic
}
+ /// Attempt to downcast contained data to a specific type
pub fn try_cast(&self) -> Option<&T> {
self.data().as_any().downcast_ref()
}
+ /// Test the type of the contained data without downcasting
pub fn is(&self) -> bool {
self.data().as_any().is::()
}
+ /// Downcast contained data, panic if it isn't the specified type
pub fn cast(&self) -> &T {
self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast")
}
+ /// Normalize the contained data
pub fn run(&self, ctx: Context) -> AtomicResult {
self.0.run(ctx)
}
diff --git a/src/interner/display.rs b/src/interner/display.rs
index 9a9f826..fbecef0 100644
--- a/src/interner/display.rs
+++ b/src/interner/display.rs
@@ -23,6 +23,7 @@ pub trait InternedDisplay {
self.bundle(i).to_string()
}
+ /// Combine with an interner to implement [Display]
fn bundle<'a>(&'a self, interner: &'a Interner) -> DisplayBundle<'a, Self> {
DisplayBundle { interner, data: self }
}
diff --git a/src/interner/token.rs b/src/interner/token.rs
index 196e7eb..8ff7c11 100644
--- a/src/interner/token.rs
+++ b/src/interner/token.rs
@@ -13,12 +13,15 @@ pub struct Tok {
phantom_data: PhantomData,
}
impl Tok {
+ /// Wrap an ID number into a token
pub fn from_id(id: NonZeroU32) -> Self {
Self { id, phantom_data: PhantomData }
}
+ /// Take the ID number out of a token
pub fn into_id(self) -> NonZeroU32 {
self.id
}
+ /// Cast into usize
pub fn into_usize(self) -> usize {
let zero: u32 = self.id.into();
zero as usize
diff --git a/src/lib.rs b/src/lib.rs
index 8ddd1a3..4719fa5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,7 @@
+#![deny(missing_docs)]
+#![doc(html_logo_url = "../logo.jpg")]
+//! Orchid is a lazy, pure scripting language to be embedded in Rust
+//! applications. Check out the repo for examples and other links.
pub mod foreign;
mod foreign_macros;
pub mod interner;
diff --git a/src/pipeline/error/module_not_found.rs b/src/pipeline/error/module_not_found.rs
index 12cc7e5..c407013 100644
--- a/src/pipeline/error/module_not_found.rs
+++ b/src/pipeline/error/module_not_found.rs
@@ -5,7 +5,9 @@ use crate::utils::BoxedIter;
/// Error produced when an import refers to a nonexistent module
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ModuleNotFound {
+ /// The file containing the invalid import
pub file: Vec,
+ /// The invalid import path
pub subpath: Vec,
}
impl ProjectError for ModuleNotFound {
diff --git a/src/pipeline/error/not_exported.rs b/src/pipeline/error/not_exported.rs
index edd264f..69eaeab 100644
--- a/src/pipeline/error/not_exported.rs
+++ b/src/pipeline/error/not_exported.rs
@@ -7,9 +7,13 @@ use crate::utils::BoxedIter;
/// An import refers to a symbol which exists but is not exported.
#[derive(Debug)]
pub struct NotExported {
+ /// The containing file - files are always exported
pub file: Vec,
+ /// The path leading to the unexported module
pub subpath: Vec,
+ /// The offending file
pub referrer_file: Vec,
+ /// The module containing the offending import
pub referrer_subpath: Vec,
}
impl ProjectError for NotExported {
diff --git a/src/pipeline/error/parse_error_with_path.rs b/src/pipeline/error/parse_error_with_path.rs
index 34a0855..1dc5d0e 100644
--- a/src/pipeline/error/parse_error_with_path.rs
+++ b/src/pipeline/error/parse_error_with_path.rs
@@ -8,8 +8,11 @@ use crate::utils::BoxedIter;
/// Produced by stages that parse text when it fails.
#[derive(Debug)]
pub struct ParseErrorWithPath {
+ /// The complete source of the faulty file
pub full_source: String,
+ /// The path to the faulty file
pub path: Vec,
+ /// The parse error produced by Chumsky
pub error: ParseError,
}
impl ProjectError for ParseErrorWithPath {
diff --git a/src/pipeline/error/project_error.rs b/src/pipeline/error/project_error.rs
index f0ab621..a80b60b 100644
--- a/src/pipeline/error/project_error.rs
+++ b/src/pipeline/error/project_error.rs
@@ -7,7 +7,9 @@ use crate::utils::BoxedIter;
/// A point of interest in resolving the error, such as the point where
/// processing got stuck, a command that is likely to be incorrect
pub struct ErrorPosition {
+ /// The suspected location
pub location: Location,
+ /// Any information about the role of this location
pub message: Option,
}
diff --git a/src/pipeline/error/too_many_supers.rs b/src/pipeline/error/too_many_supers.rs
index a44340d..fa69006 100644
--- a/src/pipeline/error/too_many_supers.rs
+++ b/src/pipeline/error/too_many_supers.rs
@@ -9,8 +9,11 @@ use crate::utils::BoxedIter;
/// than the current module's absolute path
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TooManySupers {
+ /// The offending import path
pub path: Vec,
+ /// The file containing the offending import
pub offender_file: Vec,
+ /// The module containing the offending import
pub offender_mod: Vec,
}
impl ProjectError for TooManySupers {
diff --git a/src/pipeline/error/unexpected_directory.rs b/src/pipeline/error/unexpected_directory.rs
index 9528ded..0430b47 100644
--- a/src/pipeline/error/unexpected_directory.rs
+++ b/src/pipeline/error/unexpected_directory.rs
@@ -6,6 +6,7 @@ use crate::utils::BoxedIter;
/// a path that refers to a directory
#[derive(Debug)]
pub struct UnexpectedDirectory {
+ /// Path to the offending collection
pub path: Vec,
}
impl ProjectError for UnexpectedDirectory {
diff --git a/src/pipeline/error/visibility_mismatch.rs b/src/pipeline/error/visibility_mismatch.rs
index 3d95013..de6a349 100644
--- a/src/pipeline/error/visibility_mismatch.rs
+++ b/src/pipeline/error/visibility_mismatch.rs
@@ -8,7 +8,9 @@ use crate::utils::BoxedIter;
/// Multiple occurences of the same namespace with different visibility
#[derive(Debug)]
pub struct VisibilityMismatch {
+ /// The namespace with ambiguous visibility
pub namespace: Vec,
+ /// The file containing the namespace
pub file: Rc>,
}
impl ProjectError for VisibilityMismatch {
diff --git a/src/pipeline/file_loader.rs b/src/pipeline/file_loader.rs
index 60b0131..f782d7a 100644
--- a/src/pipeline/file_loader.rs
+++ b/src/pipeline/file_loader.rs
@@ -33,10 +33,14 @@ impl ProjectError for FileLoadingError {
/// as the file system.
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Loaded {
+ /// Conceptually equivalent to a sourcefile
Code(Rc),
+ /// Conceptually equivalent to the list of *.orc files in a folder, without
+ /// the extension
Collection(Rc>),
}
impl Loaded {
+ /// Is the loaded item source code (not a collection)?
pub fn is_code(&self) -> bool {
matches!(self, Loaded::Code(_))
}
diff --git a/src/pipeline/project_tree/const_tree.rs b/src/pipeline/project_tree/const_tree.rs
index 0e6300e..c25cead 100644
--- a/src/pipeline/project_tree/const_tree.rs
+++ b/src/pipeline/project_tree/const_tree.rs
@@ -16,7 +16,9 @@ use crate::utils::{pushed, Substack};
/// describe libraries of external functions in Rust. It implements [Add] for
/// added convenience
pub enum ConstTree {
+ /// A function or constant
Const(Expr),
+ /// A submodule
Tree(HashMap, ConstTree>),
}
impl ConstTree {
diff --git a/src/representations/ast.rs b/src/representations/ast.rs
index 7eb0dcd..4505f74 100644
--- a/src/representations/ast.rs
+++ b/src/representations/ast.rs
@@ -19,7 +19,9 @@ use crate::utils::Substack;
/// A [Clause] with associated metadata
#[derive(Clone, Debug, PartialEq)]
pub struct Expr {
+ /// The actual value
pub value: Clause,
+ /// Information about the code that produced this value
pub location: Location,
}
@@ -87,7 +89,9 @@ pub enum PHClass {
/// Properties of a placeholder that matches unknown tokens in macros
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Placeholder {
+ /// Identifier to pair placeholders in the pattern and template
pub name: Tok,
+ /// The nature of the token set matched by this placeholder
pub class: PHClass,
}
@@ -321,8 +325,11 @@ impl InternedDisplay for Clause {
/// A substitution rule as read from the source
#[derive(Debug, Clone, PartialEq)]
pub struct Rule {
+ /// Tree fragment in the source code that activates this rule
pub pattern: Rc>,
+ /// Influences the order in which rules are checked
pub prio: NotNan,
+ /// Tree fragment generated by this rule
pub template: Rc>,
}
@@ -386,7 +393,9 @@ impl InternedDisplay for Rule {
/// A named constant
#[derive(Debug, Clone, PartialEq)]
pub struct Constant {
+ /// Used to reference the constant
pub name: Tok,
+ /// The constant value inserted where the name is found
pub value: Expr,
}
diff --git a/src/representations/interpreted.rs b/src/representations/interpreted.rs
index e854717..cc2e090 100644
--- a/src/representations/interpreted.rs
+++ b/src/representations/interpreted.rs
@@ -18,7 +18,9 @@ use crate::utils::sym2string;
/// An expression with metadata
pub struct Expr {
+ /// The actual value
pub clause: Clause,
+ /// Information about the code that produced this value
pub location: Location,
}
@@ -57,10 +59,20 @@ pub struct NotALiteral;
#[derive(Clone)]
pub struct ExprInst(pub Rc>);
impl ExprInst {
+ /// Read-only access to the shared expression instance
+ ///
+ /// # Panics
+ ///
+ /// if the expression is already borrowed in read-write mode
pub fn expr(&self) -> impl Deref + '_ {
self.0.as_ref().borrow()
}
+ /// Read-Write access to the shared expression instance
+ ///
+ /// # Panics
+ ///
+ /// if the expression is already borrowed
pub fn expr_mut(&self) -> impl DerefMut + '_ {
self.0.as_ref().borrow_mut()
}
@@ -140,11 +152,23 @@ pub enum Clause {
/// An unintrospectable unit
P(Primitive),
/// A function application
- Apply { f: ExprInst, x: ExprInst },
+ Apply {
+ /// Function to be applied
+ f: ExprInst,
+ /// Argument to be substituted in the function
+ x: ExprInst,
+ },
/// A name to be looked up in the interpreter's symbol table
Constant(Sym),
/// A function
- Lambda { args: Option, body: ExprInst },
+ Lambda {
+ /// A collection of (zero or more) paths to placeholders belonging to this
+ /// function
+ args: Option,
+ /// The tree produced by this function, with placeholders where the
+ /// argument will go
+ body: ExprInst,
+ },
/// A placeholder within a function that will be replaced upon application
LambdaArg,
}
diff --git a/src/representations/literal.rs b/src/representations/literal.rs
index 73fdc39..ebc03b9 100644
--- a/src/representations/literal.rs
+++ b/src/representations/literal.rs
@@ -6,9 +6,13 @@ use ordered_float::NotNan;
/// external functions
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Literal {
+ /// Any floating point number except `NaN`
Num(NotNan),
+ /// An unsigned integer; a size, index or pointer
Uint(u64),
+ /// A single utf-8 codepoint
Char(char),
+ /// A utf-8 character sequence
Str(String),
}
diff --git a/src/representations/location.rs b/src/representations/location.rs
index e5b0a5c..03873cb 100644
--- a/src/representations/location.rs
+++ b/src/representations/location.rs
@@ -8,12 +8,21 @@ use itertools::Itertools;
/// error. Meaningful within the context of a project.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Location {
+ /// Location information lost or code generated on the fly
Unknown,
+ /// Only the file is known
File(Rc>),
- Range { file: Rc>, range: Range },
+ /// Character slice of the code
+ Range {
+ /// Argument to the file loading callback that produced this code
+ file: Rc>,
+ /// Index of the unicode code points associated with the code
+ range: Range
+ },
}
impl Location {
+ /// Range, if known. If the range is known, the file is always known
pub fn range(&self) -> Option> {
if let Self::Range { range, .. } = self {
Some(range.clone())
@@ -22,6 +31,7 @@ impl Location {
}
}
+ /// File, if known
pub fn file(&self) -> Option>> {
if let Self::File(file) | Self::Range { file, .. } = self {
Some(file.clone())
diff --git a/src/representations/sourcefile.rs b/src/representations/sourcefile.rs
index c84be6b..8f74e2b 100644
--- a/src/representations/sourcefile.rs
+++ b/src/representations/sourcefile.rs
@@ -9,6 +9,12 @@ use crate::utils::{unwrap_or, BoxedIter};
/// imported or importing all available symbols with a globstar (*)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Import {
+ /// Import path, a sequence of module names. Can either start with
+ ///
+ /// - `self` to reference the current module
+ /// - any number of `super` to reference the parent module of the implied
+ /// `self`
+ /// - a root name
pub path: Sym,
/// If name is None, this is a wildcard import
pub name: Option>,
@@ -31,25 +37,37 @@ impl Import {
/// A namespace block
#[derive(Debug, Clone)]
pub struct Namespace {
+ /// Name prefixed to all names in the block
pub name: Tok,
+ /// Prefixed entries
pub body: Vec,
}
/// Things that may be prefixed with an export
#[derive(Debug, Clone)]
pub enum Member {
+ /// A substitution rule. Rules apply even when they're not in scope, if the
+ /// absolute names are present eg. because they're produced by other rules
Rule(Rule),
+ /// A constant (or function) associated with a name
Constant(Constant),
+ /// A prefixed set of other entries
Namespace(Namespace),
}
/// Anything we might encounter in a file
#[derive(Debug, Clone)]
pub enum FileEntry {
+ /// Imports one or all names in a module
Import(Vec),
+ /// Comments are kept here in case dev tooling wants to parse documentation
Comment(String),
+ /// An element visible to the outside
Exported(Member),
+ /// An element only visible from local code
Internal(Member),
+ /// A list of tokens exported explicitly. This can also create new exported
+ /// tokens that the local module doesn't actually define a role for
Export(Vec>),
}
diff --git a/src/representations/tree.rs b/src/representations/tree.rs
index 7fba86b..89f4561 100644
--- a/src/representations/tree.rs
+++ b/src/representations/tree.rs
@@ -10,23 +10,32 @@ use super::sourcefile::Import;
use crate::interner::Tok;
use crate::utils::Substack;
+/// The member in a [ModEntry] which is associated with a name in a [Module]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModMember {
+ /// Arbitrary data
Item(TItem),
+ /// A child module
Sub(Rc>),
}
+/// Data about a name in a [Module]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModEntry {
+ /// The submodule or item
pub member: ModMember,
+ /// Whether the member is visible to modules other than the parent
pub exported: bool,
}
/// A module, containing imports,
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Module {
+ /// Import statements present this module
pub imports: Vec,
+ /// Submodules and items by name
pub items: HashMap, ModEntry>,
+ /// Additional information associated with the module
pub extra: TExt,
}
diff --git a/src/rule/repository.rs b/src/rule/repository.rs
index f3c3d95..8e87ca1 100644
--- a/src/rule/repository.rs
+++ b/src/rule/repository.rs
@@ -46,6 +46,7 @@ pub struct Repository {
cache: Vec<(CachedRule, HashSet, NotNan)>,
}
impl Repository {
+ /// Build a new repository to hold the given set of rules
pub fn new(
mut rules: Vec,
i: &Interner,
diff --git a/src/stl/cpsio/io.rs b/src/stl/cpsio/io.rs
index f69b1c6..5d8033b 100644
--- a/src/stl/cpsio/io.rs
+++ b/src/stl/cpsio/io.rs
@@ -10,7 +10,9 @@ use crate::utils::unwrap_or;
/// An IO command to be handled by the host application.
#[derive(Clone, Debug)]
pub enum IO {
+ /// Print a string to standard output and resume execution
Print(String, ExprInst),
+ /// Read a line from standard input and pass it to the calback
Readline(ExprInst),
}
atomic_inert!(IO);
diff --git a/src/stl/mk_stl.rs b/src/stl/mk_stl.rs
index 1ac7c77..7ab5f6e 100644
--- a/src/stl/mk_stl.rs
+++ b/src/stl/mk_stl.rs
@@ -6,6 +6,8 @@ use super::str::str;
use crate::interner::Interner;
use crate::pipeline::ConstTree;
+/// Build the standard library used by the interpreter by combining the other
+/// libraries
pub fn mk_stl(i: &Interner) -> ConstTree {
cpsio(i) + conv(i) + bool(i) + str(i) + num(i)
}
diff --git a/src/stl/mod.rs b/src/stl/mod.rs
index 8a29177..8c0ce3d 100644
--- a/src/stl/mod.rs
+++ b/src/stl/mod.rs
@@ -1,3 +1,4 @@
+//! Constants exposed to usercode by the interpreter
mod assertion_error;
mod bool;
mod conv;
diff --git a/src/utils/side.rs b/src/utils/side.rs
index 8d7ce18..3dcfbba 100644
--- a/src/utils/side.rs
+++ b/src/utils/side.rs
@@ -7,7 +7,9 @@ use super::BoxedIter;
/// are technically usable for this purpose, they're very easy to confuse
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Side {
+ /// Left, low, or high-to-low in the case of sequences
Left,
+ /// Right, high, or low-to-high in the case of sequences
Right,
}
@@ -21,6 +23,7 @@ impl Display for Side {
}
impl Side {
+ /// Get the side that is not the current one
pub fn opposite(&self) -> Self {
match self {
Self::Left => Self::Right,
diff --git a/src/utils/substack.rs b/src/utils/substack.rs
index eed6eee..40983b5 100644
--- a/src/utils/substack.rs
+++ b/src/utils/substack.rs
@@ -17,7 +17,9 @@ pub struct Stackframe<'a, T> {
/// the recursion isn't deep enough to warrant a heap-allocated set.
#[derive(Clone, Copy)]
pub enum Substack<'a, T> {
+ /// A level in the linked list
Frame(Stackframe<'a, T>),
+ /// The end of the list
Bottom,
}
@@ -33,12 +35,16 @@ impl<'a, T> Substack<'a, T> {
pub fn iter(&self) -> SubstackIterator {
SubstackIterator { curr: self }
}
+ /// Add the item to this substack
pub fn push(&'a self, item: T) -> Self {
Self::Frame(self.new_frame(item))
}
+ /// Create a new frame on top of this substack
pub fn new_frame(&'a self, item: T) -> Stackframe<'a, T> {
Stackframe { item, prev: self, len: self.opt().map_or(1, |s| s.len) }
}
+ /// obtain the previous stackframe if one exists
+ /// TODO: this should return a [Substack]
pub fn pop(&'a self, count: usize) -> Option<&'a Stackframe<'a, T>> {
if let Self::Frame(p) = self {
if count == 0 {
@@ -50,12 +56,14 @@ impl<'a, T> Substack<'a, T> {
None
}
}
+ /// number of stackframes
pub fn len(&self) -> usize {
match self {
Self::Frame(f) => f.len,
Self::Bottom => 0,
}
}
+ /// is this the bottom of the stack
pub fn is_empty(&self) -> bool {
self.len() == 0
}