diff --git a/README.md b/README.md
index f9a0fc5c..cb5d5359 100644
--- a/README.md
+++ b/README.md
@@ -1,239 +1,88 @@
-# Compose
-
-> **Work in Progress** โ Compose is an experimental programming language and interpreter, built as a learning project to explore language design, fault-tolerant parsing, and high-quality diagnostics.
-
----
-
-## ๐ง Status
-
-Compose is **not production-ready**. Expect breaking changes, incomplete features, and rough edges.
-
-That said, the project already includes a working parser, interpreter, and a growing standard library.
-
----
-
-## โจ Goals and Features
-
-* **Fault-Tolerant Parsing**
- Composeโs parser is designed to recover gracefully from syntax errors, building a complete **Concrete Syntax Tree (CST)** even when source code contains mistakes. This enables:
-
- * Detailed error reporting
- * Non-blocking analysis tools
- * IDE-like features in the future
-
-* **Helpful Diagnostics**
- Compose provides **precise, user-friendly diagnostics** with labeled spans, suggestions, and contextual hints. Examples:
-
-```rust
-error[E0009]: expected a comma between the function arguments
- โโ main.cmps:1:14
- โ
-1 โ let x = foo(a c = d)
- โ ^ help: insert a comma here
- โ
-= help: for more information about this error, try `compose explain E0009`
-
-error[E0002]: assignments are not allowed in expression contexts
- โโ main.cmps:1:17
- โ
-1 โ let x = foo(a c = d)
- โ ^ assignment is not allowed here
- โ
-= help: if you meant to compare `c` and `d`, use `==` instead of `=`
-= help: if you meant to assign to `c`, wrap the statement in a block: `{ c = ... }`
-= help: or introduce a new variable with `let`: `{ let c = ... }`
-= note: assignments like `c = ...` are only valid as standalone statements
-= help: for more information about this error, try `compose explain E0002`
-
-error[E0006]: expected a semicolon after a statement
- โโ main.cmps:1:21
- โ
-1 โ let x = foo(a c = d)
- โ ^ help: insert a semicolon here
- โ
-= suggested fix: write a semicolon to terminate this statement:
-`let x = foo(a c = d);`
-= help: for more information about this error, try `compose explain E0006`
-
-error: condition expressions require parentheses
- โโ main.cmps:2:12
- โ
-2 โ let y = if true { 1 } else { 0 }
- โ ^^^^ Expected an opening `(` before the condition
-```
+# The Compose Programming Language
```rust
-error[E0004]: cannot reassign to a variable declared as immutable
- โโ main.cmps:3:1
- โ
-1 โ let a;
- โ - was defined as immutable here
-2 โ a = "some value";
- โ - first assignment occurred here
-3 โ a = "another value";
- โ ^ cannot reassign an immutable variable
- โ
-= help: make the variable mutable by writing `let mut`
-= note: variables are immutable by default
+# compose::test::assert_eval(r#"
+println("'Hello, world' from Compose!");
+# "#);
```
-* **Interpreter Design Inspired by Typst**
- Architecture and runtime are heavily inspired by the [Typst](https://typst.app) compiler.
-
-* **Influenced by "Writing an Interpreter in Go"**
- Thorsten Ballโs book provided foundational parsing and interpreter concepts.
-
----
-
-## ๐ Key Features of Compose
-
-Compose is **expressive, fault-tolerant, and developer-friendly**. Highlights:
-
-### ๐งฑ Statements
-
-* **Expression Statements:** Useful for side effects.
-
- ```compose
- 3 + 4; // evaluates but does nothing
- println("hi"); // prints "hi"
- ```
-
-* **Let Bindings:** Immutable by default; mutable with `let mut`.
-
- ```compose
- let x = 42;
- let mut y = 5;
- y = 6; // allowed
- ```
+Compose is a functionally flavoured interpreted programming language with Rust-like syntax.
-* **Assignments:** Must target existing variables. Compose provides clear errors for undeclared or immutable targets.
+Features:
+- Expression focused: blocks and control flow (`if`, `match`, loops) are expressions and produce values.
+- Functions as first-class citizens
+- High quality diagnostics
+- Variables are immutable by default
+- Garbage collection
+- Portable: runs on any platform that supports rust
----
+
-### โจ Expressions
+Compose is being developed as a hobby project and is not intended for production use.
-Compose treats almost everything as an expression, making code **flexible and composable**.
+
-* **Literals & Variables:**
+For more thorough documentation about the language, embedding the language in your application, or using the CLI check out the [docs](https://dutch-raptor.github.io/compose/compose/).
- ```compose
- 42; true; "hello"; x;
- ```
+# CLI Quick start
-* **Arithmetic & Logical Operators:**
+If you want to try out Compose, the easiest way is to use the CLI.
- ```compose
- 1 + 2 * 3;
- x > 5 && y != 0;
- ```
+The `compose` CLI lets you run Compose programs, explore them interactively, and inspect errors.
-* **Functions & Closures:** First-class values, can be assigned, passed, or returned.
- ```compose
- let add = { x, y => x + y; };
- add(1, 2); // returns 3
+## Installation
- let make_adder = { x => { y => x + y; } };
- let add_one = make_adder(1);
- add_one(2); // returns 3
- ```
+Install the latest version from GitHub using [Cargo](https://doc.rust-lang.org/cargo/):
-* **Blocks:** Return the value of the last expression.
-
- ```compose
- let result = { let y = 3; y + 1; }; // 4
- ```
-
-* **Conditionals:** `if` expressions produce a value.
-
- ```compose
- let x = if a > 10 { "big" } else { "small" };
- ```
-
-* **Loops:** `while` and `for` can return a value using `break`.
-
- ```compose
- let result = {
- let mut i = 0;
- while true {
- if i == 5 { break i * 2; }
- i += 1;
- }
- }; // 10
- ```
-
----
-
-### ๐ Expression Summary
-
-| Type | Returns a Value? | Example |
-| ------------------ | ---------------- | ---------------------------------- |
-| Literal | โ | `42`, `"hi"` |
-| Variable Reference | โ | `x` |
-| Arithmetic | โ | `1 + 2 * 3` |
-| Function Call | โ | `add(1, 2)` |
-| Block `{ ... }` | โ | `{ let x = 2; x + 1 }` |
-| `if` / `else` | โ | `if x > 0 { "yes" } else { "no" }` |
-| `while` / `for` | โ via `break` | `while cond { break 5; }` |
-| Closure | โ | `{ x => x + 1 }` |
-
-> **Philosophy:** if it does something, it probably returns something too.
-
----
-
-## ๐ Why Compose Exists
-
-Compose is a **learning exercise**:
+```bash
+cargo install --git https://github.com/Dutch-Raptor/compose.git
+```
-* Explore **language design**, parsing, and interpreter internals.
-* Build **robust developer tools** with IDE-quality feedback.
-* Experiment with:
+After installation, the `compose` executable will be available on your PATH:
- * Error recovery that doesnโt compromise correctness
- * Clear diagnostics with fix suggestions
- * Garbage Collection
- * Compile-time checked documentation
+```bash
+compose --version
+```
----
+## Usage
-## ๐ ๏ธ Current State
+Create a new file called `hello.cmps` with the following contents:
-* โ Hand-written fault-tolerant parser (CST-based)
-* โ Interpreter with value/reference model
-* โ Support for arrays, maps, functions, closures, boxed values
-* โ Support for cyclic data structures in runtime
-* โ Garbage collector (mark-and-sweep)
-* ๐ง Import system (single-evaluation, no cycles, partial)
-* ๐ง Iterators (some combinators implemented)
-* ๐ง Standard library growth
-* ๐ง Error message improvements and linter-like suggestions
+```rust
+println("Hello, from Compose!")
+```
----
+and run it:
-## ๐ References & Inspirations
+```bash
+compose file hello.cmps
+```
-* [Typst](https://typst.app): Clean interpreter & VM design
-* *Writing an Interpreter in Go* by Thorsten Ball
-* [codespan-reporting](http://github.com/brendanzab/codespan) and [Spade Lang fork](https://gitlab.com/spade-lang/codespan)
+This should print `Hello, from Compose!` to the console.
----
+### Run a file
-## ๐ฎ Roadmap Ideas
+```bash
+compose file examples/hello.cmps
+```
-* IDE integration via LSP
-* More expressive types and pattern matching
-* Inline documentation and REPL
-* Educational visualizations for debugging
+### Start a REPL
----
+```bash
+compose repl
+```
-## ๐ค Contributing
+Load a file in the REPL:
-Primarily a **personal learning project**, but feedback and discussions are welcome.
+```bash
+compose repl --from examples/prelude.cmps
+```
----
+### Explain an error
-## License
+```bash
+compose explain E0003
+```
-Compose is licensed under the [Apache License 2.0](LICENSE).
-You are free to use, modify, and distribute Compose in personal or commercial projects, provided you include the license text and notices.
\ No newline at end of file
+This gives examples, explanations, and suggested fixes.
\ No newline at end of file
diff --git a/compose-cli/src/file.rs b/compose-cli/src/file.rs
index 1d34800b..1b61069a 100644
--- a/compose-cli/src/file.rs
+++ b/compose-cli/src/file.rs
@@ -24,7 +24,7 @@ pub fn file(args: FileArgs) -> Result<(), CliError> {
crate::print_diagnostics(&world, &[], &warnings).unwrap();
}
- let Warned { value, warnings } = compose_eval::eval(&source, &mut vm, &EvalConfig::default());
+ let Warned { value, warnings } = compose_eval::eval_source(&source, &mut vm, &EvalConfig::default());
if let Err(err) = value {
crate::print_diagnostics(&world, &err, &warnings).unwrap();
diff --git a/compose-cli/src/repl/mod.rs b/compose-cli/src/repl/mod.rs
index e8b2bf6c..fbca92b0 100644
--- a/compose-cli/src/repl/mod.rs
+++ b/compose-cli/src/repl/mod.rs
@@ -173,7 +173,7 @@ fn eval_initial_pass(vm: &mut Machine, world: &SystemWorld) {
// Do not return early if there are any errors, as we want to print all diagnostics.
for i in 0..source.nodes().len() {
let Warned { value, warnings } =
- compose_eval::eval_range(&source, i..i + 1, vm, &EvalConfig::default());
+ compose_eval::eval_source_range(&source, i..i + 1, vm, &EvalConfig::default());
crate::print_diagnostics(world, &[], &warnings).unwrap();
if let Err(err) = value {
crate::print_diagnostics(world, &err, &warnings).unwrap();
@@ -212,7 +212,7 @@ pub fn eval_repl_input(vm: &mut Machine, world: &SystemWorld, input: &str, args:
crate::print_diagnostics(world, &[], &syntax_warnings).unwrap();
}
- let Warned { value, warnings } = compose_eval::eval_range(
+ let Warned { value, warnings } = compose_eval::eval_source_range(
&source,
len_before_edit..len_after_edit,
vm,
diff --git a/compose-doc/src/realise.rs b/compose-doc/src/realise.rs
index e6293ebc..6eb52908 100644
--- a/compose-doc/src/realise.rs
+++ b/compose-doc/src/realise.rs
@@ -7,7 +7,7 @@ pub(crate) fn eval_code(code: &str) -> EvalResult {
let world = DocWorld::from_str(code);
let mut vm = Machine::new(&world);
- let Warned { value, warnings } = compose_eval::eval(
+ let Warned { value, warnings } = compose_eval::eval_source(
&world.source,
&mut vm,
&EvalConfig {
diff --git a/compose-eval/src/expression/array.rs b/compose-eval/src/expression/array.rs
index b59b1090..06c94a70 100644
--- a/compose-eval/src/expression/array.rs
+++ b/compose-eval/src/expression/array.rs
@@ -1,6 +1,8 @@
use crate::{Eval, Machine};
use compose_library::diag::SourceResult;
-use compose_library::{ArrayValue, IntoValue, Value, Vm};
+use compose_library::{ Value, Vm};
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::types::ArrayValue;
use compose_syntax::ast;
use crate::evaluated::{Evaluated, ValueEvaluatedExtensions};
diff --git a/compose-eval/src/expression/assignment.rs b/compose-eval/src/expression/assignment.rs
index a18cddd1..7b92a31f 100644
--- a/compose-eval/src/expression/assignment.rs
+++ b/compose-eval/src/expression/assignment.rs
@@ -1,7 +1,8 @@
use crate::access::Access;
use crate::{Eval, Machine};
use compose_library::diag::{bail, At, SourceResult};
-use compose_library::{ops, Value};
+use compose_library::{Value};
+use compose_library::foundations::ops;
use compose_syntax::ast;
use compose_syntax::ast::{AssignOp, AstNode};
use crate::evaluated::Evaluated;
diff --git a/compose-eval/src/expression/atomic.rs b/compose-eval/src/expression/atomic.rs
index da30d9ee..09be5d7e 100644
--- a/compose-eval/src/expression/atomic.rs
+++ b/compose-eval/src/expression/atomic.rs
@@ -1,7 +1,7 @@
use crate::vm::Machine;
use crate::Eval;
use compose_library::diag::SourceResult;
-use compose_library::IntoValue;
+use compose_library::foundations::cast::IntoValue;
use compose_syntax::ast;
use crate::evaluated::{Evaluated, ValueEvaluatedExtensions};
diff --git a/compose-eval/src/expression/binary.rs b/compose-eval/src/expression/binary.rs
index 3a49e426..83693581 100644
--- a/compose-eval/src/expression/binary.rs
+++ b/compose-eval/src/expression/binary.rs
@@ -1,7 +1,8 @@
use crate::vm::Machine;
use crate::Eval;
use compose_library::diag::{bail, At, SourceResult};
-use compose_library::{ops, Value};
+use compose_library::{Value};
+use compose_library::foundations::ops;
use compose_syntax::ast;
use compose_syntax::ast::{AstNode, BinOp};
use crate::evaluated::{Evaluated, ValueEvaluatedExtensions};
diff --git a/compose-eval/src/expression/bindings.rs b/compose-eval/src/expression/bindings.rs
index 8661805c..c21c11e7 100644
--- a/compose-eval/src/expression/bindings.rs
+++ b/compose-eval/src/expression/bindings.rs
@@ -3,7 +3,7 @@ use crate::expression::pattern::{PatternContext, PatternMatchResult};
use crate::vm::{ErrorMode, Machine};
use crate::Eval;
use compose_library::diag::{bail, At, SourceResult};
-use compose_library::{BindingKind, Visibility};
+use compose_library::foundations::scope::{BindingKind, Visibility};
use compose_syntax::ast;
use compose_syntax::ast::AstNode;
use crate::evaluated::Evaluated;
@@ -72,10 +72,12 @@ impl<'a> Eval for ast::LetBinding<'a> {
#[cfg(test)]
mod tests {
- use super::*;
use crate::test::{assert_eval, assert_eval_with_vm, eval_code_with_vm, TestWorld};
use compose_error_codes::{E0004_MUTATE_IMMUTABLE_VARIABLE, W0001_USED_UNINITIALIZED_VARIABLE};
- use compose_library::{BindingKind, UnitValue, Value};
+ use compose_library::foundations::scope::BindingKind;
+ use compose_library::foundations::types::UnitValue;
+ use compose_library::Value;
+ use crate::Machine;
#[test]
fn test_let_binding() {
diff --git a/compose-eval/src/expression/call.rs b/compose-eval/src/expression/call.rs
index 5a563818..516fb2ad 100644
--- a/compose-eval/src/expression/call.rs
+++ b/compose-eval/src/expression/call.rs
@@ -1,11 +1,14 @@
use crate::vm::ErrorMode;
use crate::{Eval, Machine};
use compose_library::diag::{bail, At, SourceResult, Spanned, Trace, TracePoint};
-use compose_library::{Arg, Args, Func, NativeScope, Type, UnboundItem, Value};
use compose_syntax::ast::AstNode;
use compose_syntax::{ast, Label, Span};
use ecow::{eco_format, EcoString, EcoVec};
use extension_traits::extension;
+use compose_library::foundations::args::{Arg, Args};
+use compose_library::foundations::scope::{NativeScope, UnboundItem};
+use compose_library::foundations::types::{Func, Type};
+use compose_library::Value;
use crate::evaluated::Evaluated;
impl Eval for ast::FuncCall<'_> {
diff --git a/compose-eval/src/expression/captures_visitor.rs b/compose-eval/src/expression/captures_visitor.rs
index d9ede792..04ff5ab9 100644
--- a/compose-eval/src/expression/captures_visitor.rs
+++ b/compose-eval/src/expression/captures_visitor.rs
@@ -1,9 +1,10 @@
-use compose_library::{Binding, Library, Scope, Scopes, Value};
use compose_syntax::ast::{Arg, AstNode, Expr, Ident, ParamKind, Statement};
use compose_syntax::{ast, Span};
use ecow::EcoString;
use std::collections::HashMap;
use std::path::PathBuf;
+use compose_library::foundations::scope::{Binding, Scope, Scopes};
+use compose_library::{Library, Value};
/// Visits a closure and determines which variables are captured implicitly.
#[derive(Debug)]
@@ -290,7 +291,7 @@ mod tests {
use crate::expression::captures_visitor::CapturesVisitor;
use crate::test::{print_diagnostics, TestWorld};
use compose_library::diag::SourceDiagnostic;
- use compose_library::{Scope, Scopes};
+ use compose_library::foundations::scope::{Scope, Scopes};
use compose_syntax::ast;
#[track_caller]
diff --git a/compose-eval/src/expression/closure.rs b/compose-eval/src/expression/closure.rs
index 991fea10..056a942e 100644
--- a/compose-eval/src/expression/closure.rs
+++ b/compose-eval/src/expression/closure.rs
@@ -3,13 +3,15 @@ use crate::expression::pattern::{destructure_pattern, PatternContext, PatternMat
use crate::vm::{FlowEvent, TrackedContainer};
use crate::{Eval, Machine};
use compose_library::diag::{bail, error, IntoSourceDiagnostic, SourceResult, Spanned};
-use compose_library::{
- Args, Binding, BindingKind, Closure, Func, IntoValue, Scope, Value,
- VariableAccessError, Visibility,
-};
use compose_syntax::ast::{AstNode, Expr, Ident, Param, ParamKind, Pattern};
use compose_syntax::{ast, Label};
use ecow::EcoVec;
+use compose_library::foundations::args::Args;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::scope::{Binding, BindingKind, Scope, VariableAccessError, Visibility};
+use compose_library::foundations::types::Func;
+use compose_library::foundations::types::func::Closure;
+use compose_library::Value;
use crate::evaluated::{Evaluated, ValueEvaluatedExtensions};
impl Eval for ast::Lambda<'_> {
diff --git a/compose-eval/src/expression/control_flow.rs b/compose-eval/src/expression/control_flow.rs
index 667948eb..6cf2a965 100644
--- a/compose-eval/src/expression/control_flow.rs
+++ b/compose-eval/src/expression/control_flow.rs
@@ -2,7 +2,9 @@ use crate::expression::pattern::{destructure_pattern, PatternContext, PatternMat
use crate::vm::{FlowEvent, Tracked};
use crate::{Eval, Machine};
use compose_library::diag::{At, SourceResult};
-use compose_library::{bail, BindingKind, IterValue, Value, ValueIterator, Visibility};
+use compose_library::{bail, Value};
+use compose_library::foundations::iterator::{IterValue, ValueIterator};
+use compose_library::foundations::scope::{BindingKind, Visibility};
use compose_syntax::ast;
use compose_syntax::ast::AstNode;
use crate::evaluated::Evaluated;
diff --git a/compose-eval/src/expression/index_access.rs b/compose-eval/src/expression/index_access.rs
index 3fa483a3..4df60802 100644
--- a/compose-eval/src/expression/index_access.rs
+++ b/compose-eval/src/expression/index_access.rs
@@ -1,7 +1,8 @@
use crate::vm::Tracked;
use crate::{Eval, Machine};
use compose_library::diag::SourceResult;
-use compose_library::{IntoValue, Vm};
+use compose_library::{Vm};
+use compose_library::foundations::cast::IntoValue;
use compose_syntax::ast;
use compose_syntax::ast::AstNode;
use crate::evaluated::Evaluated;
diff --git a/compose-eval/src/expression/map.rs b/compose-eval/src/expression/map.rs
index c4c36762..fa02c9a6 100644
--- a/compose-eval/src/expression/map.rs
+++ b/compose-eval/src/expression/map.rs
@@ -1,9 +1,10 @@
use crate::{Eval, Machine};
use compose_library::diag::{bail, SourceResult};
-use compose_library::{MapValue, Value, Vm};
+use compose_library::{Value, Vm};
use compose_syntax::ast;
use compose_syntax::ast::{AstNode, Expr};
use std::collections::HashMap;
+use compose_library::foundations::types::MapValue;
use crate::evaluated::Evaluated;
impl Eval for ast::MapLiteral<'_> {
diff --git a/compose-eval/src/expression/pattern.rs b/compose-eval/src/expression/pattern.rs
index 76b04a88..14084217 100644
--- a/compose-eval/src/expression/pattern.rs
+++ b/compose-eval/src/expression/pattern.rs
@@ -2,11 +2,7 @@ use crate::evaluated::Evaluated;
use crate::{Eval, Machine};
use compose_error_codes::{E0007_MISSING_EQUALS_AFTER_LET_BINDING, E0301_ARRAY_DESTRUCTURING_WRONG_NUMBER_OF_ELEMENTS, E0302_MAP_DESTRUCTURING_UNCOVERED_KEYS, E0303_MAP_DESTRUCTURING_MISSING_KEY_IN_VALUE, E0304_TYPE_PATTERNS_NOT_ALLOWED_IN_LET_BINDINGS};
use compose_library::diag::{At, SourceDiagnostic, SourceResult, Spanned};
-use compose_library::ops::Comparison;
-use compose_library::{
- ArrayValue, Binding, BindingKind, DebugRepr, IntoValue, MapValue, Type, Value, Visibility, Vm,
- bail, error,
-};
+use compose_library::{bail, error, Value, Vm};
use compose_syntax::ast::{AstNode, DestructuringItem, Expr, Ident, LiteralPattern, Pattern};
use compose_syntax::{Span, ast};
use compose_utils::trace_fn;
@@ -15,6 +11,11 @@ use std::cmp::PartialEq;
use std::collections::{HashMap, HashSet};
use std::fmt::Display;
use tap::Tap;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::ops::Comparison;
+use compose_library::foundations::scope::{Binding, BindingKind, Visibility};
+use compose_library::foundations::types::{ArrayValue, MapValue, Type};
+use compose_library::foundations::value::DebugRepr;
impl Eval for LiteralPattern<'_> {
fn eval(self, vm: &mut Machine) -> SourceResult {
diff --git a/compose-eval/src/expression/range.rs b/compose-eval/src/expression/range.rs
index 59b5d9cd..1f23c7a3 100644
--- a/compose-eval/src/expression/range.rs
+++ b/compose-eval/src/expression/range.rs
@@ -1,6 +1,7 @@
use crate::{Eval, Machine};
use compose_library::diag::{At, SourceResult};
-use compose_library::{RangeValue, Value};
+use compose_library::{Value};
+use compose_library::foundations::types::RangeValue;
use compose_syntax::ast;
use compose_syntax::ast::AstNode;
use crate::evaluated::Evaluated;
diff --git a/compose-eval/src/expression/unary.rs b/compose-eval/src/expression/unary.rs
index de6fbf4c..a5b1469e 100644
--- a/compose-eval/src/expression/unary.rs
+++ b/compose-eval/src/expression/unary.rs
@@ -1,7 +1,9 @@
use crate::access::Access;
use crate::{Eval, Machine};
use compose_library::diag::{bail, At, SourceResult, StrResult};
-use compose_library::{ops, Heap, Value};
+use compose_library::{Value};
+use compose_library::foundations::ops;
+use compose_library::gc::Heap;
use compose_syntax::ast::{AstNode, UnOp, Unary};
use crate::evaluated::Evaluated;
diff --git a/compose-eval/src/lib.rs b/compose-eval/src/lib.rs
index 6cf511b5..7d404455 100644
--- a/compose-eval/src/lib.rs
+++ b/compose-eval/src/lib.rs
@@ -41,7 +41,7 @@ To make this less error-prone, [`Machine`] provides several helpers:
- [`Machine::in_flow_scope_guard`]
- [`Machine::new_flow_scope_guard`]
-To read more about the rationale behind scope usage see [`compose_library::Scopes`].
+To read more about the rationale behind scope usage see [`compose_library::foundations::scope::Scopes`].
*Example: Evaluating a code block*
@@ -159,18 +159,18 @@ pub struct EvalConfig {
pub include_syntax_warnings: bool,
}
-pub fn eval(
+pub fn eval_source(
source: &Source,
vm: &mut Machine,
eval_config: &EvalConfig,
) -> Warned> {
- eval_range(source, 0..usize::MAX, vm, eval_config)
+ eval_source_range(source, 0..usize::MAX, vm, eval_config)
}
/// Eval a source file.
///
/// eval_range: eval these nodes
-pub fn eval_range(
+pub fn eval_source_range(
source: &Source,
eval_range: Range,
vm: &mut Machine,
diff --git a/compose-eval/src/statement.rs b/compose-eval/src/statement.rs
index 1ef28c10..833eb1b9 100644
--- a/compose-eval/src/statement.rs
+++ b/compose-eval/src/statement.rs
@@ -1,12 +1,14 @@
use crate::evaluated::Evaluated;
use crate::vm::FlowEvent;
-use crate::{Eval, EvalConfig, Machine, eval};
+use crate::{Eval, EvalConfig, Machine, eval_source};
use compose_library::diag::{SourceResult, Trace, TracePoint, Warned, bail, error};
-use compose_library::{Binding, IntoValue, Module};
use compose_syntax::ast::{AstNode, BreakStatement};
use compose_syntax::{FileId, ast};
use ecow::{EcoString, eco_vec};
use std::path::PathBuf;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::module::Module;
+use compose_library::foundations::scope::Binding;
impl Eval for ast::Statement<'_> {
fn eval(self, vm: &mut Machine) -> SourceResult {
@@ -115,7 +117,7 @@ impl Eval for ast::ModuleImport<'_> {
let module = vm
.with_frame(|vm| {
- let Warned { value, warnings } = eval(&source, vm, &EvalConfig::default());
+ let Warned { value, warnings } = eval_source(&source, vm, &EvalConfig::default());
value?;
vm.sink_mut().warnings.extend(warnings);
diff --git a/compose-eval/src/test/mod.rs b/compose-eval/src/test/mod.rs
index 497b9d11..f280b0ea 100644
--- a/compose-eval/src/test/mod.rs
+++ b/compose-eval/src/test/mod.rs
@@ -159,7 +159,7 @@ pub fn eval_code_with_vm(vm: &mut Machine, world: &TestWorld, input: &str) -> Te
let source = world.entrypoint_src();
let len_after_edit = source.nodes().len();
- let Warned { value, warnings } = crate::eval_range(
+ let Warned { value, warnings } = crate::eval_source_range(
&source,
len_before_edit..len_after_edit,
vm,
diff --git a/compose-eval/src/vm/mod.rs b/compose-eval/src/vm/mod.rs
index 181e81ed..d352721f 100644
--- a/compose-eval/src/vm/mod.rs
+++ b/compose-eval/src/vm/mod.rs
@@ -2,19 +2,27 @@ mod stack;
use crate::expression::eval_lambda;
use crate::vm::stack::{StackFrames, TrackMarker};
-use compose_library::diag::{error, At, SourceDiagnostic, SourceResult};
-use compose_library::{
- Args, Binding, BindingKind, Engine, Func, FuncKind, Heap, IntoValue, Routines, Scopes, Sink,
- SyntaxContext, Trace, UntypedRef, Value, VariableAccessError, Visibility, Vm, World,
+use compose_library::diag::{At, SourceDiagnostic, SourceResult, error};
+use compose_library::engine::Engine;
+use compose_library::foundations::args::Args;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::scope::{
+ Binding, BindingKind, Scopes, VariableAccessError, Visibility,
};
+use compose_library::foundations::types::Func;
+use compose_library::foundations::types::func::FuncKind;
+use compose_library::gc::{Heap, Trace, UntypedRef};
+use compose_library::sink::Sink;
+use compose_library::world::SyntaxContext;
+use compose_library::{Value, Vm, World};
use compose_syntax::ast::AstNode;
-use compose_syntax::{ast, Span};
+use compose_syntax::{Span, ast};
use compose_utils::{defer, trace_fn};
use ecow::EcoString;
pub use stack::Tracked;
pub use stack::TrackedContainer;
use std::fmt::Debug;
-use std::ops::{DerefMut};
+use std::ops::DerefMut;
pub struct Machine<'a> {
pub frames: StackFrames<'a>,
@@ -55,7 +63,6 @@ impl<'a> Machine<'a> {
frames: StackFrames::new(Some(world.library())),
flow: None,
engine: Engine {
- routines: routines(),
sink: Sink::default(),
world,
},
@@ -81,7 +88,7 @@ impl<'a> Machine<'a> {
};
self.heap.maybe_gc(&roots);
}
-
+
pub fn sink_mut(&mut self) -> &mut Sink {
&mut self.engine.sink
}
@@ -92,7 +99,7 @@ impl<'a> Machine<'a> {
/// scope is pushed before `f` is executed and is always popped afterwards,
/// even if `f` introduces new bindings.
///
- /// # Scope behavior
+ /// # Scope behaviour
/// - Always creates a new lexical scope
/// - Scope is exited immediately after `f` returns
pub fn in_lexical_scope(&mut self, f: impl FnOnce(&mut Machine<'a>) -> T) -> T {
@@ -145,7 +152,7 @@ impl<'a> Machine<'a> {
/// - Safe to use with early returns or error propagation
#[must_use]
pub fn in_flow_scope_guard(&mut self) -> impl DerefMut {
- let _trace_guard = ::compose_utils::TraceFnGuard::new("in_flow_scope_guard", None);
+ let _trace_guard = compose_utils::TraceFnGuard::new("in_flow_scope_guard", None);
let top_was_flow = self.scopes().top_flow().is_some();
if !top_was_flow {
self.scopes_mut().enter_flow();
@@ -172,7 +179,7 @@ impl<'a> Machine<'a> {
/// - Safe across early returns and error paths
#[must_use]
pub fn new_lexical_scope_guard(&mut self) -> impl DerefMut {
- let _trace_guard = ::compose_utils::TraceFnGuard::new("lexical_scope_guard", None);
+ let _trace_guard = compose_utils::TraceFnGuard::new("lexical_scope_guard", None);
self.scopes_mut().enter_lexical();
defer(self, move |vm| {
vm.scopes_mut().exit_lexical();
@@ -196,7 +203,7 @@ impl<'a> Machine<'a> {
#[track_caller]
#[must_use]
pub fn new_flow_scope_guard(&mut self) -> impl DerefMut {
- let _trace_guard = ::compose_utils::TraceFnGuard::new("new_flow_scope_guard", None);
+ let _trace_guard = compose_utils::TraceFnGuard::new("new_flow_scope_guard", None);
self.scopes_mut().enter_flow();
defer(self, move |vm| {
vm.scopes_mut().exit_flow();
@@ -275,7 +282,10 @@ impl<'a> Machine<'a> {
pub fn try_bind(&mut self, name: EcoString, binding: Binding) -> SourceResult<&mut Binding> {
let span = binding.span();
- self.scopes_mut().top_lexical_mut().try_bind(name, binding).at(span)
+ self.scopes_mut()
+ .top_lexical_mut()
+ .try_bind(name, binding)
+ .at(span)
}
pub fn bind(&mut self, name: EcoString, binding: Binding) -> &mut Binding {
@@ -359,7 +369,6 @@ impl FlowEvent {
}
}
-
#[derive(Debug)]
pub struct VmRoots<'a> {
pub frames: &'a StackFrames<'a>,
@@ -375,11 +384,6 @@ impl Trace for VmRoots<'_> {
}
}
-
-pub fn routines() -> Routines {
- Routines {}
-}
-
#[derive(Debug, Clone, Default)]
pub struct EvalContext {
pub closure_capture: ErrorMode,
@@ -399,5 +403,4 @@ impl ErrorMode {
}
}
-impl Machine<'_> {
-}
+impl Machine<'_> {}
diff --git a/compose-eval/src/vm/stack.rs b/compose-eval/src/vm/stack.rs
index 1313f00b..bf275fc5 100644
--- a/compose-eval/src/vm/stack.rs
+++ b/compose-eval/src/vm/stack.rs
@@ -1,6 +1,8 @@
use std::fmt::{Debug, Formatter};
+use compose_library::foundations::scope::{Binding, Scopes, VariableAccessError};
+use compose_library::gc::{Trace, UntypedRef};
+use compose_library::Library;
use crate::Machine;
-use compose_library::{Binding, Library, Scopes, Trace, UntypedRef, VariableAccessError};
#[derive(Clone)]
pub struct StackFrames<'a> {
diff --git a/compose-library/src/diag.rs b/compose-library/src/diag.rs
index 57014e86..0d24f4ac 100644
--- a/compose-library/src/diag.rs
+++ b/compose-library/src/diag.rs
@@ -1,5 +1,5 @@
pub use compose_codespan_reporting;
-use compose_codespan_reporting::term::termcolor::{NoColor, WriteColor};
+use compose_codespan_reporting::term::termcolor::{Ansi, NoColor, WriteColor};
use compose_codespan_reporting::{diagnostic, term};
use compose_syntax::{
FileId, Fix, FixDisplay, Label, LabelType, PatchEngine, Span, SyntaxError, SyntaxErrorSeverity,
@@ -118,6 +118,22 @@ pub fn write_diagnostics_to_string(
output.trim_end().to_string()
}
+pub fn print_diagnostics(
+ world: &dyn World,
+ errors: &[SourceDiagnostic],
+ warnings: &[SourceDiagnostic],
+ ascii_only: bool,
+) -> Result<(), compose_codespan_reporting::files::Error> {
+ let mut stderr = io::stderr();
+ let writer: &mut dyn WriteColor = if ascii_only {
+ &mut NoColor::new(&mut stderr)
+ } else {
+ &mut Ansi::new(&mut stderr)
+ };
+
+ write_diagnostics(world, errors, warnings, writer, &Config::default())
+}
+
fn diag_label(diag: &SourceDiagnostic) -> Option> {
let id = diag.span.id()?;
let range = diag.span.range()?;
diff --git a/compose-library/src/engine.rs b/compose-library/src/engine.rs
index 75e72d57..b2afe654 100644
--- a/compose-library/src/engine.rs
+++ b/compose-library/src/engine.rs
@@ -1,29 +1,23 @@
-use compose_library::{Routines, Sink, SyntaxContext, World};
+use compose_library::World;
+use compose_library::sink::Sink;
+use compose_library::world::SyntaxContext;
use std::fmt::Debug;
pub struct Engine<'a> {
- pub routines: Routines,
pub world: &'a dyn World,
pub sink: Sink,
}
impl<'a> Engine<'a> {
pub fn syntax_ctx(&self) -> SyntaxContext<'_> {
- SyntaxContext {
- world: self.world
- }
+ SyntaxContext { world: self.world }
}
}
impl Debug for Engine<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Engine")
- .field("sink", &self.sink)
- .finish()
+ f.debug_struct("Engine").field("sink", &self.sink).finish()
}
}
-pub struct Routes {
-
-}
-
+pub struct Routes {}
diff --git a/compose-library/src/foundations/args.rs b/compose-library/src/foundations/args.rs
index b983474c..03de5916 100644
--- a/compose-library/src/foundations/args.rs
+++ b/compose-library/src/foundations/args.rs
@@ -1,10 +1,11 @@
-use crate::diag::{bail, error, At, SourceDiagnostic, SourceResult, Spanned};
-use crate::foundations::cast::FromValue;
-use crate::foundations::IntoValue;
-use crate::{Trace, Value};
-use compose_library::UntypedRef;
+use crate::{
+ Value,
+ diag::{At, SourceDiagnostic, SourceResult, Spanned, bail, error},
+ foundations::{cast::FromValue, cast::IntoValue},
+ gc::{Trace, UntypedRef},
+};
use compose_syntax::Span;
-use ecow::{eco_vec, EcoVec};
+use ecow::{EcoVec, eco_vec};
#[derive(Debug, Clone)]
pub struct Args {
@@ -60,7 +61,7 @@ impl Args {
}
let value = self.items.remove(i).value;
let span = value.span;
- return T::from_value(value).at(span).map(Some)
+ return T::from_value(value).at(span).map(Some);
}
Ok(None)
}
@@ -90,7 +91,7 @@ impl Args {
let value = self.items.remove(i).value;
let span = value.span;
let casted = T::from_value(value).at(span)?;
-
+
if found.is_none() {
found = Some(casted);
}
@@ -165,7 +166,7 @@ pub struct Meta(u8);
impl Meta {
const REF: Meta = Meta(1);
const MUT: Meta = Meta(2);
-
+
pub fn new() -> Meta {
Meta(0)
}
diff --git a/compose-library/src/foundations/cast/mod.rs b/compose-library/src/foundations/cast/mod.rs
index 4c7a555e..b6b35a2d 100644
--- a/compose-library/src/foundations/cast/mod.rs
+++ b/compose-library/src/foundations/cast/mod.rs
@@ -3,7 +3,7 @@ mod into_value;
mod into_result;
use crate::diag::{Spanned, StrResult};
-use crate::{UnitValue, Value};
+use crate::{foundations::types::UnitValue, Value};
use compose_macros::cast;
pub use into_result::*;
pub use into_value::*;
diff --git a/compose-library/src/foundations/cast/reflect.rs b/compose-library/src/foundations/cast/reflect.rs
index 9f7ed30f..5eac46ad 100644
--- a/compose-library/src/foundations/cast/reflect.rs
+++ b/compose-library/src/foundations/cast/reflect.rs
@@ -1,5 +1,5 @@
use crate::repr::separated_list;
-use compose_library::{Type, Value};
+use compose_library::{foundations::types::Type, Value};
use ecow::{eco_format, EcoString};
use std::fmt::Write;
diff --git a/compose-library/src/foundations/iterator/array_iter.rs b/compose-library/src/foundations/iterator/array_iter.rs
index fcc77a18..0bd1e528 100644
--- a/compose-library/src/foundations/iterator/array_iter.rs
+++ b/compose-library/src/foundations/iterator/array_iter.rs
@@ -1,8 +1,10 @@
-use crate::{Array, Trace};
-use compose_library::{UntypedRef, Value, ValueIterator, Vm};
+use compose_library::{Value, Vm};
use ecow::EcoVec;
use std::sync::{Arc, Mutex};
use compose_library::diag::SourceResult;
+use compose_library::foundations::iterator::ValueIterator;
+use compose_library::foundations::types::Array;
+use compose_library::gc::{Trace, UntypedRef};
#[derive(Debug, Clone)]
pub struct ArrayIter {
diff --git a/compose-library/src/foundations/iterator/iter_combinators.rs b/compose-library/src/foundations/iterator/iter_combinators.rs
index d3b835b4..2e2b51bb 100644
--- a/compose-library/src/foundations/iterator/iter_combinators.rs
+++ b/compose-library/src/foundations/iterator/iter_combinators.rs
@@ -1,9 +1,13 @@
use crate::diag::StrResult;
-use crate::IterValue;
-use compose_library::diag::{bail, At, SourceResult};
-use compose_library::support::eval_predicate;
+use compose_library::Value;
+use compose_library::diag::{At, SourceResult, bail};
+use compose_library::foundations::args::Args;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::iterator::{IterValue, ValueIterator};
+use compose_library::foundations::support::eval_predicate;
+use compose_library::foundations::types::{ArrayValue, Func};
+use compose_library::gc::{Trace, UntypedRef};
use compose_library::vm::Vm;
-use compose_library::{Args, ArrayValue, Func, IntoValue, Trace, UntypedRef, Value, ValueIterator};
use std::iter;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
@@ -23,22 +27,22 @@ impl TakeIter {
}
}
-// impl PartialEq for TakeIter {
-// fn eq(&self, other: &Self) -> bool {
-// if self.inner != other.inner {
-// return false;
-// }
-//
-// let take_a = self.take.lock().expect("mutex poisoned");
-// let take_b = other.take.lock().expect("mutex poisoned");
-//
-// if *take_a != *take_b {
-// return false;
-// }
-//
-// true
-// }
-// }
+impl PartialEq for TakeIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.inner != other.inner {
+ return false;
+ }
+
+ let take_a = self.take.lock().expect("mutex poisoned").clone();
+ let take_b = other.take.lock().expect("mutex poisoned").clone();
+
+ if take_a != take_b {
+ return false;
+ }
+
+ true
+ }
+}
impl Trace for TakeIter {
fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
@@ -71,24 +75,24 @@ pub struct SkipIter {
pub(crate) skip: Arc>,
}
-// impl PartialEq for SkipIter {
-// fn eq(&self, other: &Self) -> bool {
-// if self.inner != other.inner {
-// return false;
-// }
-//
-// {
-// let skip_a = self.skip.lock().expect("mutex poisoned");
-// let skip_b = self.skip.lock().expect("mutex poisoned");
-//
-// if *skip_a != *skip_b {
-// return false;
-// }
-// }
-//
-// true
-// }
-// }
+impl PartialEq for SkipIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.inner != other.inner {
+ return false;
+ }
+
+ {
+ let skip_a = self.skip.lock().expect("mutex poisoned").clone();
+ let skip_b = self.skip.lock().expect("mutex poisoned").clone();
+
+ if skip_a != skip_b {
+ return false;
+ }
+ }
+
+ true
+ }
+}
impl Trace for SkipIter {
fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
@@ -153,6 +157,16 @@ impl ValueIterator for TakeWhileIter {
// nth method cannot be optimized here, so we just fall back to the default
}
+impl PartialEq for TakeWhileIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.inner != other.inner {
+ return false;
+ }
+
+ self.predicate == other.predicate
+ }
+}
+
#[derive(Debug, Clone)]
pub struct FilterIter {
pub(crate) inner: IterValue,
@@ -186,6 +200,16 @@ impl ValueIterator for FilterIter {
// Fall back to the default implementation
}
+impl PartialEq for FilterIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.inner != other.inner {
+ return false;
+ }
+
+ self.predicate == other.predicate
+ }
+}
+
#[derive(Debug, Clone)]
pub struct MapIter {
pub(crate) inner: IterValue,
@@ -217,6 +241,12 @@ impl ValueIterator for MapIter {
}
}
+impl PartialEq for MapIter {
+ fn eq(&self, other: &Self) -> bool {
+ self.inner == other.inner && self.map == other.map
+ }
+}
+
impl StepByIter {
pub fn new(inner: IterValue, step: usize) -> StrResult {
if step == 0 {
@@ -231,23 +261,26 @@ impl StepByIter {
}
}
-// impl PartialEq for StepByIter {
-// fn eq(&self, other: &Self) -> bool {
-// if self.step != other.step {
-// return false;
-// }
-//
-// if *self.first_step.lock().unwrap() != *other.first_step.lock().unwrap() {
-// return false;
-// }
-//
-// if self.inner != other.inner {
-// return false;
-// }
-//
-// true
-// }
-// }
+impl PartialEq for StepByIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.step != other.step {
+ return false;
+ }
+
+ if self.inner != other.inner {
+ return false;
+ }
+
+ let self_first_step = self.first_step.lock().expect("lock poisened").clone();
+ let other_first_step = other.first_step.lock().expect("lock poisened").clone();
+
+ if self_first_step != other_first_step {
+ return false;
+ }
+
+ true
+ }
+}
#[derive(Debug, Clone)]
pub struct StepByIter {
@@ -327,3 +360,16 @@ impl ValueIterator for EnumerateIter {
))
}
}
+
+impl PartialEq for EnumerateIter {
+ fn eq(&self, other: &Self) -> bool {
+ if self.inner.iter != other.inner.iter {
+ return false;
+ }
+
+ let self_index = self.index.lock().expect("index poisoned").clone();
+ let other_index = other.index.lock().expect("index poisoned").clone();
+
+ self_index == other_index
+ }
+}
diff --git a/compose-library/src/foundations/iterator/mod.rs b/compose-library/src/foundations/iterator/mod.rs
index 6843e836..975145ae 100644
--- a/compose-library/src/foundations/iterator/mod.rs
+++ b/compose-library/src/foundations/iterator/mod.rs
@@ -1,8 +1,5 @@
-use crate::{ArrayValue, HeapRef, MapValue, Trace};
-use crate::{UntypedRef, Value};
use compose_library::diag::{SourceResult, bail, error};
use compose_library::vm::Vm;
-use compose_library::{Func, Str};
use compose_macros::{func, scope, ty};
use compose_syntax::Span;
use std::collections::HashMap;
@@ -14,10 +11,15 @@ mod iter_combinators;
mod range_iter;
mod string_iter;
-use crate::diag::{SourceDiagnostic, StrResult, UnSpanned};
-use crate::support::eval_func;
+use crate::{
+ foundations::support::eval_func,
+ diag::{SourceDiagnostic, StrResult, UnSpanned},
+ Value,
+ foundations::support::eval_predicate
+};
pub use array_iter::*;
-use compose_library::support::eval_predicate;
+use compose_library::foundations::types::{ArrayValue, Func, MapValue, Str};
+use compose_library::gc::{HeapRef, Trace, UntypedRef};
pub use iter_combinators::*;
pub use range_iter::*;
pub use string_iter::*;
@@ -109,7 +111,7 @@ pub fn requires_mutable_iter(value: Value) -> Result compose_library::diag::StrResult;
+ fn equals(&self, other: &Self, heap: &Heap) -> compose_library::diag::StrResult;
fn not_equals(&self, other: &Self, heap: &Heap) -> StrResult {
self.equals(other, heap).map(|b| !b)
diff --git a/compose-library/src/foundations/ops/mod.rs b/compose-library/src/foundations/ops/mod.rs
index 524e50e1..0808e738 100644
--- a/compose-library/src/foundations/ops/mod.rs
+++ b/compose-library/src/foundations/ops/mod.rs
@@ -2,9 +2,9 @@ mod equality;
use crate::Value;
use crate::diag::StrResult;
-use compose_library::Heap;
-pub use compose_library::ops::equality::*;
+pub use compose_library::foundations::ops::equality::*;
use ecow::eco_format;
+use compose_library::gc::Heap;
macro_rules! type_error {
($fmt:expr, $($value:expr),* $(,)?) => {
diff --git a/compose-library/src/foundations/scope.rs b/compose-library/src/foundations/scope.rs
index 615c4b3a..66af6cc0 100644
--- a/compose-library/src/foundations/scope.rs
+++ b/compose-library/src/foundations/scope.rs
@@ -1,11 +1,8 @@
use crate::diag::{At, IntoSourceDiagnostic, SourceDiagnostic, SourceResult, error, warning};
-use crate::{IntoValue, Trace};
-use crate::{Library, NativeFuncData, NativeType, Sink, Type, Value};
use compose_error_codes::{
E0004_MUTATE_IMMUTABLE_VARIABLE, E0011_UNBOUND_VARIABLE, W0001_USED_UNINITIALIZED_VARIABLE,
};
use compose_library::diag::{StrResult, bail};
-use compose_library::{Func, NativeFunc, UntypedRef};
use compose_syntax::{Label, Span};
use compose_utils::trace_log;
use ecow::{EcoString, eco_format, eco_vec};
@@ -17,6 +14,12 @@ use std::hash::Hash;
use std::sync::LazyLock;
use strsim::jaro_winkler;
use tap::Pipe;
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::types::NativeType;
+use compose_library::foundations::types::{Func, func::NativeFunc, func::NativeFuncData, Type};
+use compose_library::gc::{Trace, UntypedRef};
+use compose_library::{Library, Value};
+use compose_library::sink::Sink;
pub trait NativeScope {
fn scope() -> &'static Scope;
diff --git a/compose-library/src/foundations/support.rs b/compose-library/src/foundations/support.rs
index c5f548fe..20b7b260 100644
--- a/compose-library/src/foundations/support.rs
+++ b/compose-library/src/foundations/support.rs
@@ -1,7 +1,9 @@
use compose_error_codes::E0012_PREDICATE_MUST_RETURN_BOOLEAN;
use compose_library::diag::{SourceResult, bail, error};
-use compose_library::{Args, Func, Value, Vm};
use std::iter;
+use compose_library::foundations::types::Func;
+use compose_library::{Value, Vm};
+use compose_library::foundations::args::Args;
pub fn eval_predicate(
vm: &mut dyn Vm,
diff --git a/compose-library/src/foundations/array.rs b/compose-library/src/foundations/types/array.rs
similarity index 95%
rename from compose-library/src/foundations/array.rs
rename to compose-library/src/foundations/types/array.rs
index de0fa0d5..63fd2eae 100644
--- a/compose-library/src/foundations/array.rs
+++ b/compose-library/src/foundations/types/array.rs
@@ -1,11 +1,12 @@
use crate::repr::Repr;
-use crate::{HeapRef, Iter, IterValue, Trace};
use compose_library::diag::{At, SourceResult, StrResult};
-use compose_library::{ArrayIter, Heap, UntypedRef, Value, Vm};
use compose_macros::{func, scope, ty};
use compose_syntax::Span;
use ecow::{eco_format, EcoString};
use std::ops::{Deref, DerefMut};
+use compose_library::gc::{Heap, HeapRef, Trace, UntypedRef};
+use compose_library::{Value, Vm};
+use compose_library::foundations::iterator::{ArrayIter, Iter, IterValue};
#[ty(scope, cast, name = "Array")]
#[derive(Debug, Clone, PartialEq)]
diff --git a/compose-library/src/foundations/bool.rs b/compose-library/src/foundations/types/bool.rs
similarity index 100%
rename from compose-library/src/foundations/bool.rs
rename to compose-library/src/foundations/types/bool.rs
diff --git a/compose-library/src/foundations/boxed.rs b/compose-library/src/foundations/types/boxed.rs
similarity index 95%
rename from compose-library/src/foundations/boxed.rs
rename to compose-library/src/foundations/types/boxed.rs
index da59ad6e..17d4e027 100644
--- a/compose-library/src/foundations/boxed.rs
+++ b/compose-library/src/foundations/types/boxed.rs
@@ -1,6 +1,5 @@
use crate::gc::HeapRef;
-use crate::UntypedRef;
-use compose_library::gc::Heap;
+use compose_library::gc::{Heap, UntypedRef};
use compose_library::vm::Vm;
use compose_library::Value;
use compose_macros::func;
diff --git a/compose-library/src/foundations/func.rs b/compose-library/src/foundations/types/func.rs
similarity index 98%
rename from compose-library/src/foundations/func.rs
rename to compose-library/src/foundations/types/func.rs
index ebbf0c58..a45b01f4 100644
--- a/compose-library/src/foundations/func.rs
+++ b/compose-library/src/foundations/types/func.rs
@@ -1,10 +1,8 @@
use crate::diag::{SourceResult, StrResult, bail};
use crate::foundations::args::Args;
use crate::vm::Vm;
-use crate::{Sink, Trace, Value};
use compose_error_codes::E0010_UNCAPTURED_VARIABLE;
use compose_library::diag::{Spanned, error};
-use compose_library::{Scope, UntypedRef};
use compose_macros::{cast, ty};
use compose_syntax::ast::{AstNode};
use compose_syntax::{Label, Span, SyntaxNode, ast};
@@ -14,6 +12,10 @@ use std::collections::HashMap;
use std::fmt;
use std::sync::LazyLock;
use tap::Tap;
+use compose_library::foundations::scope::Scope;
+use compose_library::gc::{Trace, UntypedRef};
+use compose_library::sink::Sink;
+use compose_library::Value;
#[derive(Clone, Debug, PartialEq)]
#[ty(cast)]
diff --git a/compose-library/src/foundations/int.rs b/compose-library/src/foundations/types/int.rs
similarity index 96%
rename from compose-library/src/foundations/int.rs
rename to compose-library/src/foundations/types/int.rs
index 86119cc9..0748eb1d 100644
--- a/compose-library/src/foundations/int.rs
+++ b/compose-library/src/foundations/types/int.rs
@@ -1,4 +1,4 @@
-use crate::diag::bail;
+use compose_library::bail;
use compose_library::diag::StrResult;
use compose_library::Value;
use compose_macros::{cast, func};
diff --git a/compose-library/src/foundations/map.rs b/compose-library/src/foundations/types/map.rs
similarity index 92%
rename from compose-library/src/foundations/map.rs
rename to compose-library/src/foundations/types/map.rs
index 5e46f3a2..618f99b4 100644
--- a/compose-library/src/foundations/map.rs
+++ b/compose-library/src/foundations/types/map.rs
@@ -1,13 +1,15 @@
use crate::repr::Repr;
-use crate::{ArrayIter, Heap, Trace};
-use compose_library::{HeapRef, IntoValue, IterValue, UntypedRef, Value, Vm};
use compose_macros::func;
use compose_macros::{scope, ty};
use ecow::{EcoString, EcoVec};
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use compose_library::diag::StrResult;
-use compose_library::ops::Comparison;
+use compose_library::gc::{Heap, HeapRef, Trace, UntypedRef};
+use compose_library::{Value, Vm};
+use compose_library::foundations::cast::IntoValue;
+use compose_library::foundations::iterator::{ArrayIter, IterValue};
+use compose_library::foundations::ops::Comparison;
#[ty(scope, cast, name = "Map")]
#[derive(Debug, Clone, PartialEq)]
diff --git a/compose-library/src/foundations/types/mod.rs b/compose-library/src/foundations/types/mod.rs
new file mode 100644
index 00000000..e523ab92
--- /dev/null
+++ b/compose-library/src/foundations/types/mod.rs
@@ -0,0 +1,20 @@
+pub mod array;
+pub mod bool;
+pub mod str;
+pub mod func;
+pub mod ty;
+pub mod int;
+pub mod unit;
+pub mod boxed;
+pub mod range;
+pub mod map;
+
+pub use array::{ArrayValue, Array};
+pub use func::{Func};
+pub use crate::foundations::module::{Module};
+pub use ty::{Type, NativeType, NativeTypeData};
+pub use unit::{UnitValue};
+pub use boxed::Boxed;
+pub use range::{RangeValue, Range, RangeImpl};
+pub use map::{Map, MapValue};
+pub use str::Str;
\ No newline at end of file
diff --git a/compose-library/src/foundations/range.rs b/compose-library/src/foundations/types/range.rs
similarity index 95%
rename from compose-library/src/foundations/range.rs
rename to compose-library/src/foundations/types/range.rs
index 583627bb..cfa7a553 100644
--- a/compose-library/src/foundations/range.rs
+++ b/compose-library/src/foundations/types/range.rs
@@ -1,10 +1,12 @@
use crate::repr::Repr;
-use crate::{FromValue, Iter, Trace};
use compose_library::diag::{StrResult, bail};
-use compose_library::{IntoValue, IterValue, RangeIter, UntypedRef, Value, Vm};
use compose_macros::{func, scope, ty};
use ecow::{EcoString, eco_format};
use std::sync::Arc;
+use compose_library::foundations::cast::{FromValue, IntoValue};
+use compose_library::gc::{Trace, UntypedRef};
+use compose_library::{Value, Vm};
+use compose_library::foundations::iterator::{Iter, IterValue, RangeIter};
#[ty(scope, cast, name = "range")]
#[derive(Clone, Debug, PartialEq)]
diff --git a/compose-library/src/foundations/str.rs b/compose-library/src/foundations/types/str.rs
similarity index 91%
rename from compose-library/src/foundations/str.rs
rename to compose-library/src/foundations/types/str.rs
index 140d1f30..bac3bd07 100644
--- a/compose-library/src/foundations/str.rs
+++ b/compose-library/src/foundations/types/str.rs
@@ -1,13 +1,12 @@
-use crate::Iter;
-use compose_library::diag::bail;
+use compose_library::{bail, Value};
use compose_library::repr::Repr;
use compose_library::vm::Vm;
-use compose_library::{IterValue, StringIterator, Value};
use compose_macros::func;
use compose_macros::{cast, scope, ty};
use ecow::EcoString;
use std::fmt;
use std::ops::Add;
+use compose_library::foundations::iterator::{Iter, IterValue, StringIterator};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[ty(scope, cast, name = "String")]
@@ -94,6 +93,12 @@ cast! {
v: Str => v.into()
}
+cast! {
+ String,
+ self => Value::Str(self.into()),
+ v: Str => v.into()
+}
+
cast! {
char,
self => Value::Str(EcoString::from(self).into()),
diff --git a/compose-library/src/foundations/ty.rs b/compose-library/src/foundations/types/ty.rs
similarity index 91%
rename from compose-library/src/foundations/ty.rs
rename to compose-library/src/foundations/types/ty.rs
index 6a12984c..29bf7421 100644
--- a/compose-library/src/foundations/ty.rs
+++ b/compose-library/src/foundations/types/ty.rs
@@ -1,12 +1,14 @@
-use crate::{Scope, Sink, Trace, UnBoundError, Value};
use compose_library::diag::StrResult;
-use compose_library::{UnboundItem, UntypedRef};
use compose_macros::{cast, ty};
use compose_syntax::Span;
use compose_utils::Static;
use ecow::eco_format;
use std::fmt::Display;
use std::sync::LazyLock;
+use compose_library::foundations::scope::{Scope, UnBoundError, UnboundItem};
+use compose_library::gc::{Trace, UntypedRef};
+use compose_library::sink::Sink;
+use compose_library::Value;
#[derive(Clone, Debug, PartialEq)]
#[ty(cast, name = "type")]
diff --git a/compose-library/src/foundations/unit.rs b/compose-library/src/foundations/types/unit.rs
similarity index 100%
rename from compose-library/src/foundations/unit.rs
rename to compose-library/src/foundations/types/unit.rs
diff --git a/compose-library/src/foundations/value.rs b/compose-library/src/foundations/value.rs
index 63893ca3..e8a0c719 100644
--- a/compose-library/src/foundations/value.rs
+++ b/compose-library/src/foundations/value.rs
@@ -1,19 +1,20 @@
use crate::diag::{At, SourceResult, Spanned};
-use crate::foundations::boxed::Boxed;
-use crate::{CastInfo, Str, SyntaxContext, UnitValue};
-use crate::{FromValue, Func};
-use crate::{IntoValue, Vm};
-use crate::{NativeScope, Reflect};
-use crate::{Sink, Type};
use compose_library::diag::{StrResult, bail, error};
-use compose_library::foundations::range::RangeValue;
use compose_library::repr::Repr;
-use compose_library::{Args, ArrayValue, Heap, IterValue, MapValue, Module};
use compose_macros::func;
use compose_macros::scope;
use compose_syntax::Span;
use ecow::{EcoString, eco_format};
use std::{fmt, iter};
+use compose_library::foundations::args::Args;
+use compose_library::foundations::cast::FromValue;
+use compose_library::foundations::iterator::IterValue;
+use compose_library::foundations::scope::NativeScope;
+use compose_library::foundations::types::{*};
+use compose_library::gc::Heap;
+use compose_library::sink::Sink;
+use compose_library::Vm;
+use compose_library::world::SyntaxContext;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
@@ -267,13 +268,13 @@ macro_rules! primitive {
$ty:ty: $name:literal, $variant:ident
$(, $other:ident$(($binding:ident))? => $out:expr)*
) => {
- impl Reflect for $ty {
- fn input() -> CastInfo {
- CastInfo::Type(Type::of::())
+ impl compose_library::foundations::cast::Reflect for $ty {
+ fn input() -> compose_library::foundations::cast::CastInfo {
+ compose_library::foundations::cast::CastInfo::Type(Type::of::())
}
- fn output() -> CastInfo {
- CastInfo::Type(Type::of::())
+ fn output() -> compose_library::foundations::cast::CastInfo {
+ compose_library::foundations::cast::CastInfo::Type(Type::of::())
}
fn castable(value: &Value) -> bool {
@@ -282,18 +283,18 @@ macro_rules! primitive {
}
}
- impl IntoValue for $ty {
+ impl compose_library::foundations::cast::IntoValue for $ty {
fn into_value(self) -> Value {
Value::$variant(self)
}
}
- impl FromValue for $ty {
+ impl compose_library::foundations::cast::FromValue for $ty {
fn from_value(value: Value) -> StrResult {
match value {
Value::$variant(v) => Ok(v),
$(Value::$other$(($binding))? => Ok($out),)*
- v => Err(::error(&v)),
+ v => Err(::error(&v)),
}
}
}
diff --git a/compose-library/src/gc/clean.rs b/compose-library/src/gc/clean.rs
index 46be3790..fec25343 100644
--- a/compose-library/src/gc/clean.rs
+++ b/compose-library/src/gc/clean.rs
@@ -1,12 +1,12 @@
use compose_library::gc::trigger::GcEvent;
-use compose_library::{Heap, Trace};
use slotmap::SecondaryMap;
use std::collections::VecDeque;
use std::time::{Duration, Instant};
+use compose_library::gc::{Heap, Trace};
impl Heap {
pub fn maybe_gc(&mut self, root: &impl Trace) -> Option {
- if !self.policy.on_event(&GcEvent::MaybeGc, &self.data()) {
+ if !self.policy.on_event(&GcEvent::MaybeGc, &self.metadata()) {
return None;
}
Some(self.clean(root))
@@ -54,7 +54,7 @@ impl Heap {
gc_duration,
};
- self.policy.after_gc(&result, &self.data());
+ self.policy.after_gc(&result, &self.metadata());
result
}
diff --git a/compose-library/src/gc/mod.rs b/compose-library/src/gc/mod.rs
index cd0b6da9..dd610194 100644
--- a/compose-library/src/gc/mod.rs
+++ b/compose-library/src/gc/mod.rs
@@ -1,14 +1,71 @@
mod clean;
mod trigger;
+use crate::Value;
use crate::gc::trigger::{GcTriggerPolicy, SimplePolicy};
-use crate::{Array, Value};
+use compose_library::foundations::iterator::Iter;
+use compose_library::foundations::types::{Array, Map};
use compose_library::gc::trigger::GcData;
-use compose_library::{Iter, Map};
-use slotmap::{new_key_type, SlotMap};
+use slotmap::{SlotMap, new_key_type};
use std::fmt::Debug;
use std::ops::Deref;
+/// A managed heap containing [`HeapItem`]s accessible through [`UntypedRef`] or [`HeapRef`] keys.
+///
+/// The heap implements garbage collection through a simple mark and sweep algorithm. When cleaning the roots need to be provided.
+/// These roots are any type implementing the [Trace] trait. Anything not accessible through the provided roots will be deallocated.
+///
+/// # Example: Interacting with heap values
+///
+/// ```
+/// use compose_library::{gc::Heap, Value, gc::HeapItem};
+/// let mut heap = Heap::new();
+///
+/// let value = Value::Int(6);
+///
+/// // Allocate a value on the heap and get a key to it.
+/// let key = heap.alloc(value);
+///
+/// // Now the value is accessible through the key.
+/// assert_eq!(heap.get(key), Some(&Value::Int(6)));
+///
+/// let value_mut = heap.get_mut(key).unwrap();
+/// *value_mut = Value::Int(7);
+///
+/// // The value is updated and anyone with the key will see the new value
+/// assert_eq!(heap.get(key), Some(&Value::Int(7)));
+///
+/// // Remove the value from the heap. This will give ownership of the value to the caller.
+/// let removed_item = heap.remove(key);
+///
+/// assert_eq!(removed_item, Some(HeapItem::Value(Value::Int(7))));
+/// assert_eq!(heap.get(key), None);
+/// ```
+///
+/// # Example: Garbage Collection
+///
+/// ```
+/// use compose_library::gc::{Heap, UntypedRef};
+/// use compose_library::Value;
+/// let mut heap = Heap::new();
+///
+/// let key = heap.alloc(Value::Int(6));
+/// assert_eq!(heap.metadata().heap_size, 1);
+/// assert_eq!(heap.get(key), Some(&Value::Int(6)));
+///
+/// // clean while passing the key to the value as a simple root
+/// heap.clean(&key);
+///
+/// // The value is still accessible and the heap size hasn't changed
+/// assert_eq!(heap.metadata().heap_size, 1);
+/// assert_eq!(heap.get(key), Some(&Value::Int(6)));
+///
+/// // clean while passing a root that doesn't have access to the value
+/// heap.clean(&None::);
+///
+/// assert_eq!(heap.metadata().heap_size, 0);
+/// assert_eq!(heap.get(key), None);
+/// ```
#[derive(Debug)]
pub struct Heap {
map: SlotMap,
@@ -21,6 +78,9 @@ pub struct HeapRef {
_marker: std::marker::PhantomData,
}
+// Manually implement clone and copy for HeapRef because
+// the derive macro puts a Clone requirement on T, which is not
+// actually required
impl Clone for HeapRef {
fn clone(&self) -> Self {
HeapRef {
@@ -70,24 +130,24 @@ where
{
#[track_caller]
pub fn get_unwrap(self, heap: &Heap) -> &T {
- heap.get(self).unwrap_or_else(|| {
- panic!("Use after free. This is a bug. Key: {:?}", self.key)
- })
+ heap.get(self)
+ .unwrap_or_else(|| panic!("Use after free. This is a bug. Key: {:?}", self.key))
}
-
+
pub fn try_get(self, heap: &Heap) -> StrResult<&T> {
- heap.get(self).ok_or_else(|| "Use after free. This is a bug.".into())
+ heap.get(self)
+ .ok_or_else(|| "Use after free. This is a bug.".into())
}
- #[track_caller]
+ #[track_caller]
pub fn get_mut_unwrap(self, heap: &mut Heap) -> &mut T {
- heap.get_mut(self).unwrap_or_else(|| {
- panic!("Use after free. This is a bug. Key: {:?}", self.key)
- })
+ heap.get_mut(self)
+ .unwrap_or_else(|| panic!("Use after free. This is a bug. Key: {:?}", self.key))
}
-
+
pub fn try_get_mut(self, heap: &mut Heap) -> StrResult<&mut T> {
- heap.get_mut(self).ok_or_else(|| "Use after free. This is a bug.".into())
+ heap.get_mut(self)
+ .ok_or_else(|| "Use after free. This is a bug.".into())
}
}
@@ -97,7 +157,7 @@ impl Heap {
map: SlotMap::with_key(),
policy: Box::new(SimplePolicy {
heap_size_threshold: 500,
- })
+ }),
}
}
@@ -108,7 +168,7 @@ impl Heap {
self.map.insert(value.to_untyped()).into()
}
- pub fn data(&self) -> GcData {
+ pub fn metadata(&self) -> GcData {
GcData {
heap_size: self.map.len(),
}
@@ -121,7 +181,7 @@ impl Heap {
pub fn remove(&mut self, key: HeapRef) -> Option {
self.map.remove(*key)
}
-
+
pub fn get_untyped(&self, key: UntypedRef) -> Option<&HeapItem> {
self.map.get(key)
}
@@ -140,6 +200,26 @@ impl Trace for &[UntypedRef] {
}
}
+impl Trace for HeapRef {
+ fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
+ f(self.key())
+ }
+}
+
+impl Trace for UntypedRef {
+ fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
+ f(*self)
+ }
+}
+
+impl Trace for Option {
+ fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
+ if let Some(v) = self {
+ v.visit_refs(f)
+ }
+ }
+}
+
pub trait HeapObject: Trace + Debug {
fn from_untyped(untyped: &HeapItem) -> Option<&Self>;
fn from_untyped_mut(untyped: &mut HeapItem) -> Option<&mut Self>;
@@ -225,7 +305,7 @@ impl_heap_obj!(Array, Array);
impl_heap_obj!(Map, Map);
heap_enum! {
- #[derive(Debug, Clone)]
+ #[derive(Debug, Clone, PartialEq)]
pub enum HeapItem {
Value(Value),
Iter(Iter),
diff --git a/compose-library/src/lib.rs b/compose-library/src/lib.rs
index aeb0cae7..8a22e5d2 100644
--- a/compose-library/src/lib.rs
+++ b/compose-library/src/lib.rs
@@ -1,20 +1,27 @@
// Workaround to refer to self as compose_library instead of crate. Needed for some macros
extern crate self as compose_library;
pub mod diag;
-mod world;
-mod foundations;
-mod sink;
+pub mod world;
+pub mod foundations;
+pub mod sink;
pub mod repr;
-mod engine;
-mod gc;
-mod vm;
-pub use engine::*;
-pub use foundations::*;
-pub use gc::*;
-pub use sink::*;
+pub mod engine;
+pub mod gc;
+pub mod vm;
+
+pub use diag::{SourceResult, SourceDiagnostic};
+pub use world::{World};
+pub use foundations::{Value};
+pub use vm::{Vm};
use std::fmt::Debug;
-pub use vm::*;
-pub use world::*;
+use compose_library::{
+ foundations::global_funcs::{assert, panic, print, println},
+ foundations::iterator::IterValue,
+ foundations::module::Module,
+ foundations::scope::Scope,
+ foundations::types::{ArrayValue, Boxed, Func, MapValue, RangeValue, Str, Type},
+ gc::{Trace, UntypedRef}
+};
#[derive(Clone)]
pub struct Library {
@@ -22,6 +29,14 @@ pub struct Library {
pub global: Module,
}
+impl Library {
+ pub fn empty() -> Self {
+ Self {
+ global: Module::new("std", Scope::new_lexical())
+ }
+ }
+}
+
impl Debug for Library {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Library")
@@ -34,9 +49,6 @@ impl Trace for Library {
}
}
-pub struct Routines {
-}
-
pub fn library() -> Library {
let mut global = Scope::new_lexical();
diff --git a/compose-library/src/vm.rs b/compose-library/src/vm.rs
index 5c24fe1e..568470fa 100644
--- a/compose-library/src/vm.rs
+++ b/compose-library/src/vm.rs
@@ -1,6 +1,9 @@
+use compose_library::engine::Engine;
+use compose_library::foundations::args::Args;
+use compose_library::foundations::types::Func;
+use compose_library::gc::Heap;
+use compose_library::Value;
use crate::diag::SourceResult;
-use crate::{Args, Engine};
-use compose_library::{Func, Heap, Value};
pub trait Vm<'a> {
fn heap(&self) -> &Heap;
diff --git a/compose-macros/src/cast.rs b/compose-macros/src/cast.rs
index 709d08fc..49a7d999 100644
--- a/compose-macros/src/cast.rs
+++ b/compose-macros/src/cast.rs
@@ -16,16 +16,16 @@ pub fn cast(stream: TokenStream) -> Result {
let reflect_impl = (!input.from_value.is_empty()).then_some({
quote! {
- impl #foundations::Reflect for #ty {
+ impl #foundations::cast::Reflect for #ty {
fn castable(value: foundations::Value) -> bool {
#castable_body
}
- fn input() -> #foundations::CastInfo {
+ fn input() -> #foundations::cast::CastInfo {
#input_body
}
- fn output() -> #foundations::CastInfo {
+ fn output() -> #foundations::cast::CastInfo {
#output_body
}
}
@@ -34,7 +34,7 @@ pub fn cast(stream: TokenStream) -> Result {
let into_value = input.into_value.map(|expr| {
quote! {
- impl #foundations::IntoValue for #ty {
+ impl #foundations::cast::IntoValue for #ty {
fn into_value(self) -> #foundations::Value {
#expr
}
@@ -49,18 +49,18 @@ pub fn cast(stream: TokenStream) -> Result {
let expr = &cast.expr;
quote! {
- if <#ty as #foundations::Reflect>::castable(&value) {
- let #pattern = <#ty as #foundations::FromValue>::from_value(value)?;
+ if <#ty as #foundations::cast::Reflect>::castable(&value) {
+ let #pattern = <#ty as #foundations::cast::FromValue>::from_value(value)?;
return Ok(#expr);
}
}
});
quote! {
- impl #foundations::FromValue for #ty {
+ impl #foundations::cast::FromValue for #ty {
fn from_value(value: #foundations::Value) -> ::compose_library::diag::StrResult {
#(#cast_checks)*
- Err(::error(&value))
+ Err(::error(&value))
}
}
}
@@ -75,7 +75,7 @@ pub fn cast(stream: TokenStream) -> Result {
fn create_output_body() -> TokenStream {
quote! {
- ::input()
+ ::input()
}
}
@@ -83,7 +83,7 @@ fn create_input_body(input: &CastData) -> TokenStream {
let infos = input.from_value.iter().map(|cast| {
let ty = &cast.ty;
quote! {
- <#ty as #foundations::Reflect>::input()
+ <#ty as #foundations::cast::Reflect>::input()
}
});
@@ -96,7 +96,7 @@ fn create_castable_body(input: &CastData) -> TokenStream {
let casts = input.from_value.iter().map(|cast| {
let ty = &cast.ty;
quote! {
- if <#ty as #foundations::Reflect>::castable(&value) {
+ if <#ty as #foundations::cast::Reflect>::castable(&value) {
return true;
}
}
diff --git a/compose-macros/src/func.rs b/compose-macros/src/func.rs
index d4b89563..0eec4a89 100644
--- a/compose-macros/src/func.rs
+++ b/compose-macros/src/func.rs
@@ -23,9 +23,9 @@ fn create(func: &Func, item: &ItemFn) -> TokenStream {
let data_impl = if function_type.is_some() {
quote! {
- impl ::compose_library::foundations::NativeFunc for #rust_name {
- fn data() -> &'static ::compose_library::foundations::NativeFuncData {
- static DATA: #foundations::NativeFuncData = #data;
+ impl ::compose_library::foundations::types::func::NativeFunc for #rust_name {
+ fn data() -> &'static #foundations::types::func::NativeFuncData {
+ static DATA: #foundations::types::func::NativeFuncData = #data;
&DATA
}
}
@@ -33,8 +33,8 @@ fn create(func: &Func, item: &ItemFn) -> TokenStream {
} else {
let ident_data = quote::format_ident!("{rust_name}_data");
quote! {
- #vis fn #ident_data() -> &'static #foundations::NativeFuncData {
- static DATA: #foundations::NativeFuncData = #data;
+ #vis fn #ident_data() -> &'static #foundations::types::func::NativeFuncData {
+ static DATA: #foundations::types::func::NativeFuncData = #data;
&DATA
}
}
@@ -80,23 +80,23 @@ fn create_func_data(func: &Func) -> TokenStream {
} = func;
let scope = if *scope {
- quote! { <#rust_name as #foundations::NativeScope>::scope() }
+ quote! { <#rust_name as #foundations::scope::NativeScope>::scope() }
} else {
- quote! { foundations::EMPTY_SCOPE }
+ quote! { foundations::scope::EMPTY_SCOPE }
};
let closure = create_wrapper_closure(func);
let fn_type = match special.self_ {
- Some(Param { binding: Binding::RefMut, ..}) => quote! { #foundations::FuncType::MethodMut },
- Some(_) => quote! { #foundations::FuncType::Method },
- None => quote! { #foundations::FuncType::Associated },
+ Some(Param { binding: Binding::RefMut, ..}) => quote! { #foundations::types::func::FuncType::MethodMut },
+ Some(_) => quote! { #foundations::types::func::FuncType::Method },
+ None => quote! { #foundations::types::func::FuncType::Associated },
};
let name = quote! { #name };
quote! {
- ::compose_library::foundations::NativeFuncData {
+ #foundations::types::func::NativeFuncData {
name: #name,
closure: #closure,
scope: ::std::sync::LazyLock::new(|| #scope),
@@ -142,7 +142,7 @@ fn create_wrapper_closure(func: &Func) -> TokenStream {
#arg_handlers
#finish
let ret = #call;
- ::compose_library::foundations::IntoResult::into_result(ret, args.span)
+ #foundations::cast::IntoResult::into_result(ret, args.span)
}
}
}
diff --git a/compose-macros/src/scope.rs b/compose-macros/src/scope.rs
index 09fef59c..c3f82aab 100644
--- a/compose-macros/src/scope.rs
+++ b/compose-macros/src/scope.rs
@@ -49,10 +49,10 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result {
Ok(quote! {
#base
- impl #foundations::NativeScope for #self_ty {
- fn scope() -> &'static #foundations::Scope {
- static SCOPE: ::std::sync::LazyLock<#foundations::Scope> = ::std::sync::LazyLock::new(|| {
- let mut scope = #foundations::Scope::new_lexical();
+ impl #foundations::scope::NativeScope for #self_ty {
+ fn scope() -> &'static #foundations::scope::Scope {
+ static SCOPE: ::std::sync::LazyLock<#foundations::scope::Scope> = ::std::sync::LazyLock::new(|| {
+ let mut scope = #foundations::scope::Scope::new_lexical();
#(#definitions;)*
scope
});
@@ -85,7 +85,7 @@ fn rewrite_primitive_base(item: &ItemImpl, ident_ext: &Ident) -> TokenStream {
let ident_data = quote::format_ident!("{}_data", sig.ident);
sigs.push(quote! { #sig; });
sigs.push(quote! {
- fn #ident_data() -> &'static #foundations::NativeFuncData;
+ fn #ident_data() -> &'static #foundations::types::func::NativeFuncData;
});
}
diff --git a/compose-macros/src/ty.rs b/compose-macros/src/ty.rs
index 876733a1..7bf9995d 100644
--- a/compose-macros/src/ty.rs
+++ b/compose-macros/src/ty.rs
@@ -43,13 +43,13 @@ fn create(ty: &Type, item: Option<&syn::Item>) -> TokenStream {
});
let scope = if meta.scope {
- quote! { <#ident as #foundations::NativeScope>::scope() }
+ quote! { <#ident as #foundations::scope::NativeScope>::scope() }
} else {
- quote! { foundations::EMPTY_SCOPE }
+ quote! { foundations::scope::EMPTY_SCOPE }
};
let data = quote! {
- #foundations::NativeTypeData {
+ #foundations::types::NativeTypeData {
name: #name,
title: #title,
docs: #docs,
@@ -69,11 +69,11 @@ fn create(ty: &Type, item: Option<&syn::Item>) -> TokenStream {
#item
#cast
- impl #foundations::NativeType for #ident {
+ impl #foundations::types::NativeType for #ident {
const NAME: &'static str = #name;
- fn data() -> &'static #foundations::NativeTypeData {
- static DATA: #foundations::NativeTypeData = #data;
+ fn data() -> &'static #foundations::types::NativeTypeData {
+ static DATA: #foundations::types::NativeTypeData = #data;
&DATA
}
}
diff --git a/compose-syntax/src/file.rs b/compose-syntax/src/file.rs
index a9b51a60..30bd2765 100644
--- a/compose-syntax/src/file.rs
+++ b/compose-syntax/src/file.rs
@@ -42,6 +42,10 @@ impl VirtualPath {
pub fn display(&self) -> String {
self.0.display().to_string()
}
+
+ pub fn to_path_buf(&self) -> PathBuf {
+ self.0.clone()
+ }
}
impl Debug for VirtualPath {
diff --git a/compose-syntax/src/source.rs b/compose-syntax/src/source.rs
index 8710cd89..8cbb15d0 100644
--- a/compose-syntax/src/source.rs
+++ b/compose-syntax/src/source.rs
@@ -22,8 +22,8 @@ impl Source {
Self::new(FileId::new(path), text)
}
- pub fn from_string(name: &str, text: String) -> Self {
- Self::new(FileId::fake(name), text)
+ pub fn from_string(name: &str, text: impl Into) -> Self {
+ Self::new(FileId::fake(name), text.into())
}
pub fn new(file_id: FileId, text: String) -> Self {
diff --git a/compose/src/lib.rs b/compose/src/lib.rs
index 0a88c44e..01d763d7 100644
--- a/compose/src/lib.rs
+++ b/compose/src/lib.rs
@@ -7,31 +7,155 @@ println("'Hello, world' from Compose!");
# "#);
```
-A functional flavoured interpreted programming language with rust-like syntax.
+Compose is a functionally flavoured interpreted programming language with Rust-like syntax.
Features:
-- Expression focused: blocks and control flow are expressed as expressions.
-- Functions as first class citizens
+- Expression focused: blocks and control flow (`if`, `match`, loops) are expressions and produce values.
+- Functions as first-class citizens
- High quality diagnostics
- Variables are immutable by default
- Garbage collection
- Portable: runs on any platform that supports rust
-**This language is still in development, and APIs are subject to change. Developed as a learning
-project, the language is not intended for production use.**
+
+
+Compose is being developed as a hobby project and is not intended for production use.
+
+
To learn about the language, see the [language] module.
+To learn about how compose is implemented internally, see the [implementation] docs.
+
+# Using this crate
+
+Add compose to your dependencies:
+
+```toml
+[dependencies]
+compose = { git = "https://github.com/Dutch-Raptor/compose.git" }
+```
+
+To make Compose portable, all interaction with the outside world goes through the [`World`] trait.
+
+Let's start by implementing that.
+
+```rust
+use compose::{
+ World,
+ eval_source,
+ eval::EvalConfig,
+ eval::Machine,
+ library::Library,
+ library::repr::Repr,
+ library::diag::{FileError, FileResult, print_diagnostics},
+ syntax::FileId,
+ syntax::Source,
+ library::{Value}
+};
+use std::collections::HashMap;
+use std::io::{Read, Write};
+
+struct ExampleWorld {
+ /// The entrypoint
+ main: FileId,
+ /// Any other sources that have been loaded
+ sources: HashMap,
+ library: Library,
+}
+
+impl World for ExampleWorld {
+ fn entry_point(&self) -> FileId {
+ self.main
+ }
+
+ fn source(&self, file_id: FileId) -> FileResult {
+ match self.sources.get(&file_id).cloned() {
+ Some(source) => Ok(source),
+ None => Err(FileError::NotFound(file_id.path().to_path_buf())),
+ }
+ }
+
+ fn library(&self) -> &Library {
+ &self.library
+ }
+
+ fn write(
+ &self,
+ f: &mut dyn FnMut(&mut dyn Write) -> std::io::Result<()>,
+ ) -> std::io::Result<()> {
+ let mut stdout_lock = std::io::stdout().lock();
+ f(&mut stdout_lock)
+ }
+
+ fn read(
+ &self,
+ f: &mut dyn FnMut(&mut dyn Read) -> std::io::Result<()>,
+ ) -> std::io::Result<()> {
+ let mut stdin_lock = std::io::stdin().lock();
+ f(&mut stdin_lock)
+ }
+}
-To learn about the implementation, see the [implementation] docs.
+impl ExampleWorld {
+ fn new(main_source: Source) -> Self {
+ Self {
+ main: main_source.id(),
+ sources: {
+ let mut map = HashMap::new();
+ map.insert(main_source.id(), main_source);
+ map
+ },
+ library: Library::empty(),
+ }
+ }
+}
+
+fn main() {
+ // Define/load the source to be interpreted
+ let source_text = r#"
+ println("Hello from Compose");
+ 2 + 3 // the value of the last expression is returned
+ "#;
+
+ let file_name = "main.cmps";
+ let source = Source::from_string(file_name, source_text);
+
+ // Define the world that contains the source
+ let world = ExampleWorld::new(source.clone());
+ // Create a VM with access to that world.
+ let mut vm = Machine::new(&world);
+
+ // evaluate the source
+ let warned_result = eval_source(&source, &mut vm, &EvalConfig::default());
+
+
+ print_diagnostics(&world, &[], &warned_result.warnings, false)
+ .expect("Failed to print diagnostics");
+
+ let result = match warned_result.value {
+ Ok(value) => value,
+ Err(errors) => {
+ print_diagnostics(&world, &errors, &[], false)
+ .expect("Failed to print diagnostics");
+ return;
+ }
+ };
+
+ println!("{}", result.repr(&vm));
+
+ assert_eq!(Value::Int(5), result);
+}
+```
*/
-pub mod language;
pub mod implementation;
+pub mod language;
-pub use compose_eval::{eval, eval_range, test};
+pub use compose_eval::{eval_source, eval_source_range, test};
+pub use compose_library::{SourceResult, World, diag::SourceDiagnostic, diag::Warned};
-#[doc(hidden)]
-pub use compose_syntax as syntax;
#[doc(hidden)]
pub use compose_eval as eval;
#[doc(hidden)]
pub use compose_library as library;
+#[doc(hidden)]
+pub use compose_syntax as syntax;