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;