From 6f5a9d05dd850f5cb9625ca34f4ded2668e213a1 Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Sun, 28 May 2023 17:24:56 +0100 Subject: [PATCH] Completed docs, added icon --- Cargo.toml | 2 +- README.md | 2 +- icon.svg | 157 ++++++++++++++++++++ src/foreign.rs | 11 ++ src/interner/display.rs | 1 + src/interner/token.rs | 3 + src/lib.rs | 4 + src/pipeline/error/module_not_found.rs | 2 + src/pipeline/error/not_exported.rs | 4 + src/pipeline/error/parse_error_with_path.rs | 3 + src/pipeline/error/project_error.rs | 2 + src/pipeline/error/too_many_supers.rs | 3 + src/pipeline/error/unexpected_directory.rs | 1 + src/pipeline/error/visibility_mismatch.rs | 2 + src/pipeline/file_loader.rs | 4 + src/pipeline/project_tree/const_tree.rs | 2 + src/representations/ast.rs | 9 ++ src/representations/interpreted.rs | 28 +++- src/representations/literal.rs | 4 + src/representations/location.rs | 12 +- src/representations/sourcefile.rs | 18 +++ src/representations/tree.rs | 9 ++ src/rule/repository.rs | 1 + src/stl/cpsio/io.rs | 2 + src/stl/mk_stl.rs | 2 + src/stl/mod.rs | 1 + src/utils/side.rs | 3 + src/utils/substack.rs | 8 + 28 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 icon.svg 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 }