Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic optimizations #249

Merged
merged 142 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from 100 commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
8beb09b
Unbox needs fields in HIR
MarcelGarus Oct 21, 2022
31b323a
Move CountableId and IdGenerator to global utils
MarcelGarus Oct 21, 2022
975d196
Change HIR from LinkedHashMap to HashMap and Vec
MarcelGarus Oct 22, 2022
4dec474
Add MIR
MarcelGarus Oct 22, 2022
1fd6bfc
Remove MIR
MarcelGarus Oct 22, 2022
0ba8464
Add optimize module
MarcelGarus Oct 22, 2022
63a1454
Improve constant folding
MarcelGarus Oct 22, 2022
bbd73f7
Resolve references referring to outer expressions
MarcelGarus Oct 22, 2022
c913c73
Support inlining
MarcelGarus Oct 22, 2022
d2bf967
Don't tree-shake impure expressions
MarcelGarus Oct 22, 2022
cd6f756
Add complexity measure
MarcelGarus Oct 22, 2022
dbd4c8d
Clean up code of constant folding
MarcelGarus Oct 22, 2022
d1165fb
Move UsePath into module module
MarcelGarus Oct 22, 2022
8192cd4
Remove unused imports
MarcelGarus Oct 22, 2022
fb12a12
Implement module folding
MarcelGarus Oct 22, 2022
0c324c9
Change expression representation in HIR
MarcelGarus Oct 23, 2022
08e2c32
Add MIR
MarcelGarus Oct 23, 2022
1e74d55
Adjust how responsibilities are passed to functions
MarcelGarus Oct 23, 2022
cd4dfda
Migrate optimization utilities to work on MIR
MarcelGarus Oct 23, 2022
84c2f4c
Migrate optimization harness to MIR
MarcelGarus Oct 24, 2022
90d5b46
Implement following references
MarcelGarus Oct 24, 2022
129d894
Make opimizer utils more generally usable
MarcelGarus Oct 24, 2022
bab6b6a
Implement tree shaking
MarcelGarus Oct 24, 2022
5db3a3a
Implement constant folding
MarcelGarus Oct 24, 2022
d44640a
Improve logging
MarcelGarus Oct 24, 2022
6b16c47
Implement inlining
MarcelGarus Oct 25, 2022
a9d2c36
Clean up code
MarcelGarus Oct 25, 2022
dff8a99
Implement module folding
MarcelGarus Oct 25, 2022
3d5dddb
Implement common subtree elimination
MarcelGarus Oct 25, 2022
a99b844
Make MIR self-contained
MarcelGarus Oct 27, 2022
a9efc93
Implement reference following
MarcelGarus Oct 27, 2022
10f6cd0
Implement tree shaking
MarcelGarus Oct 27, 2022
aaf46bb
Implement constant folding
MarcelGarus Oct 27, 2022
2008c09
Add Expression::Multiple and implement flattening
MarcelGarus Oct 27, 2022
65c7a82
Implement inlining
MarcelGarus Oct 30, 2022
7f354c0
Implement inlining
MarcelGarus Oct 30, 2022
bb43ecb
Implement module folding
MarcelGarus Oct 30, 2022
8380e40
Merge branch 'main' into optimize
MarcelGarus Oct 31, 2022
e541ff1
Implement constant lifting
MarcelGarus Nov 1, 2022
6433001
Add doc comments
MarcelGarus Nov 1, 2022
bd5f696
Clean up code
MarcelGarus Nov 3, 2022
f5bd921
Integrate optimizations into query framework
MarcelGarus Nov 5, 2022
7d04fa6
Add tracing instructions
MarcelGarus Nov 5, 2022
6f0a49a
Optimize constant lifting
MarcelGarus Nov 5, 2022
ed860f2
Add second responsibility to needs
MarcelGarus Nov 5, 2022
982537b
Make tracing instructions configurable
MarcelGarus Nov 5, 2022
46a0141
Rename MIR's FoundFuzzableClosure
MarcelGarus Nov 6, 2022
34a80c7
Expend needs in MIR
MarcelGarus Nov 7, 2022
e06bca4
Remove fuzzable attribute from MIR lambdas
MarcelGarus Nov 7, 2022
4868ada
Add ModuleStarts and ModuleEnds expression to MIR
MarcelGarus Nov 7, 2022
7efed94
Clean up code
MarcelGarus Nov 7, 2022
348c553
Lower MIR to LIR
MarcelGarus Nov 7, 2022
51abe51
Rename responsibility to HirId
MarcelGarus Nov 8, 2022
ba56b15
Make IDs fancier
MarcelGarus Nov 9, 2022
39e6215
Remove HIR-to-LIR
MarcelGarus Nov 9, 2022
bed15ec
Improve caching
MarcelGarus Nov 9, 2022
05ebfe5
Make trace instructions more lightweight
MarcelGarus Nov 9, 2022
cb49710
Fix basic VM functionality
MarcelGarus Nov 10, 2022
711ddb5
Updates the surrounding code to work with MIR
MarcelGarus Nov 10, 2022
4eddc13
Fix some clippy lints
MarcelGarus Nov 10, 2022
0faf9f4
Fix clippy lints
MarcelGarus Nov 10, 2022
8f132b7
Describe MIR in overview
MarcelGarus Nov 10, 2022
c74bd0b
Make register vs. found fuzzable closures consistent
MarcelGarus Nov 10, 2022
22ea6f9
Merge branch 'add-ci' into optimize
MarcelGarus Nov 11, 2022
c547bec
Merge branch 'add-ci' into optimize
MarcelGarus Nov 11, 2022
3dceee6
Merge branch 'add-ci' into optimize
MarcelGarus Nov 11, 2022
56960dc
Merge branch 'add-ci' into optimize
MarcelGarus Nov 11, 2022
9761bcd
Merge branch 'main' into optimize
MarcelGarus Nov 11, 2022
b2ef599
Remove unused import
MarcelGarus Nov 11, 2022
134716d
Handle tooling responsibilities better
MarcelGarus Nov 11, 2022
666b8e6
Make panic reasons more consistent
MarcelGarus Nov 11, 2022
c5dfc23
Also flatten Multiples in Lambdas
MarcelGarus Nov 12, 2022
1ed9274
Test multiple flattening
MarcelGarus Nov 12, 2022
53722ab
Make tracing configurable from CLI
MarcelGarus Nov 12, 2022
823d60f
Format panic reason better
MarcelGarus Nov 12, 2022
6b71756
Fix pipe example
MarcelGarus Nov 13, 2022
fa566e9
Make stack trace formatting more robust
MarcelGarus Nov 13, 2022
a54553f
Make panic reasons more consistent
MarcelGarus Nov 13, 2022
cf4740a
Refactor trace instructions
MarcelGarus Nov 13, 2022
9427ce5
Better debugging
MarcelGarus Nov 13, 2022
5f8d886
Fix clippy lints
MarcelGarus Nov 13, 2022
3d8ffd6
Fix panic instruction in fiber
MarcelGarus Nov 14, 2022
cf4a813
Improve the runtime error messages
MarcelGarus Nov 14, 2022
2081745
Better logging
MarcelGarus Nov 14, 2022
acc910d
Make the builtinPrint more permissive
MarcelGarus Nov 16, 2022
850acf2
Better logging
MarcelGarus Nov 16, 2022
584263f
Support stdin
MarcelGarus Nov 16, 2022
049e2c2
Export run and doNotRun functions
MarcelGarus Nov 16, 2022
41bb30d
Replace single test file with more examples
MarcelGarus Nov 16, 2022
bcdf064
Fix clippy lints
MarcelGarus Nov 16, 2022
137e6cb
Use correct config for fuzzing
MarcelGarus Nov 16, 2022
56971f3
Create cheaper version of visit
MarcelGarus Nov 16, 2022
9b7d131
Fix import
MarcelGarus Nov 16, 2022
340f497
Make visitor more efficient
MarcelGarus Nov 16, 2022
1a88139
Fix benchmark code
MarcelGarus Nov 16, 2022
73f225d
Fix sorting of completely constant MIRs
MarcelGarus Nov 16, 2022
b92b33c
Implement simple module stack cancelling
MarcelGarus Nov 16, 2022
a090a36
Inline more functions
MarcelGarus Nov 16, 2022
4d543c9
Implement more constant folding
MarcelGarus Nov 16, 2022
c12b19b
Add optimize_opinionated method
MarcelGarus Nov 16, 2022
c62c794
Reorder MIR code
MarcelGarus Nov 17, 2022
bf56e1d
Don't lift top-level constants
MarcelGarus Nov 17, 2022
17276f2
Remove redundant return references
MarcelGarus Nov 17, 2022
9686520
Inline tiny functions
MarcelGarus Nov 17, 2022
9847095
Remove unused code
MarcelGarus Nov 17, 2022
fd5b63d
Fix wording of higher-lower game
MarcelGarus Nov 17, 2022
e85d08c
Fix stdout and stdin
MarcelGarus Nov 17, 2022
7a14ac7
Update compiler/src/compiler/README.md
MarcelGarus Nov 17, 2022
c647007
Apply suggestions from code review
MarcelGarus Nov 17, 2022
f7f254f
Apply some suggested changes
MarcelGarus Nov 17, 2022
06b14db
Apply more suggestions
MarcelGarus Nov 17, 2022
df7f5cc
Fix reference following
MarcelGarus Nov 17, 2022
b646450
Remove unused imports
MarcelGarus Nov 17, 2022
06dce77
Fix fuzzer
MarcelGarus Nov 17, 2022
dc17290
Fix min and max
MarcelGarus Nov 17, 2022
4df592e
Enable semantic tokens
MarcelGarus Nov 21, 2022
27c11c4
Remove some verbose tracing
MarcelGarus Nov 21, 2022
785bcc6
Clean up code
MarcelGarus Nov 22, 2022
235b0b4
Fix value hints
MarcelGarus Nov 23, 2022
0fb4f50
Increase hints server channel capacity
MarcelGarus Nov 23, 2022
b7f52a9
Fuzz more generously
MarcelGarus Nov 24, 2022
76c2944
Add debug option to fuzzing
MarcelGarus Nov 24, 2022
ef31ece
Fix several errors found through fuzzing
MarcelGarus Nov 24, 2022
40aa964
Improve debug prints
MarcelGarus Nov 24, 2022
49bdeb4
Improve formatting of fuzzer in hints server
MarcelGarus Nov 24, 2022
1b30327
Fix fuzzing
MarcelGarus Nov 24, 2022
94bd36f
Rename closure to callee where appropriate
MarcelGarus Nov 24, 2022
e72cd34
Apply suggestions from code review
MarcelGarus Nov 24, 2022
61b6119
Implement TracingConfig::none instead of deriving Default
MarcelGarus Nov 24, 2022
9d83789
Apply some suggestions
MarcelGarus Nov 24, 2022
f5b8029
Document cleanup
MarcelGarus Nov 24, 2022
0d98fd7
Improve semantically_equals for parameters
MarcelGarus Nov 24, 2022
f9ed3dc
Fix struct access constant folding
MarcelGarus Nov 24, 2022
f0ac500
Move test_multiple_flattening
MarcelGarus Nov 24, 2022
d704373
Make cancel_out_module_expresssions more efficient
MarcelGarus Nov 24, 2022
7beac1a
Implement suggestions
MarcelGarus Nov 24, 2022
05a05b0
Implement some more suggestions
MarcelGarus Nov 24, 2022
35b9f47
Fix some things
MarcelGarus Nov 24, 2022
f978bbd
Rename optimize module to mir_optimize
MarcelGarus Dec 1, 2022
2019a2d
Add comment for inline_functions_containing_use
MarcelGarus Dec 1, 2022
ad6efb5
Support trailing commas
MarcelGarus Dec 1, 2022
1254df8
Apply suggestions
MarcelGarus Dec 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ Candy blurs the line between those stages, for example, by replacing compile-tim
```candy
foo a = # If you pass a = 0,
needs (isInt a)
math.logarithm a # then this fails because logarithm only works on positive numbers.
math.logarithm a # then this panics: The `input` must be a positive number.

efficientTextReverse text =
needs (isText text)
needs (isPalindrome text) "efficientTextReverse only works on palindromes"
needs (isPalindrome text) "Only palindromes can be efficiently reversed."
text

greetBackwards name = # If you pass name = "Test",
"Hello, {efficientTextReverse name}" # then this fails because efficientTextReverse only works on palindromes.
"Hello, {efficientTextReverse name}" # then this panics: Only palindromes can be efficiently reversed.
```

To get a more in-depth introduction, read the [language document](language.md).
Expand Down Expand Up @@ -135,16 +135,13 @@ We already have a language server that provides some tooling.

## Short-term TODOs

- fix fault attribution
- new name?
- add caching while compile-time evaluating code
- tags
- pattern matching
- add CI
- add tests
- add a more lightweight tracer that only tracks stack traces
- text interpolation
- optimize: eliminate common subtrees
- optimize: inline functions
- minimize inputs found through fuzzing
- fuzz parser
Expand All @@ -155,6 +152,8 @@ We already have a language server that provides some tooling.
- distinguish packages from normal modules
- complain about comment lines with too much indentation
- develop guidelines about how to format reasons
- disallow passing named closures as parameters? or auto-propagate caller's fault to called parameters?
- replace occurrences of `Id::complicated_responsibility()`

## How to use Candy

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/compiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ These are the compiler stages:
* RCST ("Raw Concrete Syntax Tree"): A tree that represents the syntax of the code, including every single character and whitespace.
* CST ("Concrete Syntax Tree"): Similar to RCST, but tree nodes also have IDs and know what ranges in the source file they correspond to.
* AST ("Abstract Syntax Tree"): A tree where unnecessary cruft is removed and some invariants are validated.
* HIR ("High-level Intermediate Representation"): The canonical representation of source code in single-static-assignment form (SSA).
* LIR ("Low-level Intermediate Representation"): An instruction code for a stack-based virtual machine.
* HIR ("High-Level Intermediate Representation"): The canonical representation of source code in single-static-assignment form (SSA).
* MIR ("Mid-Level Intermediate Representation"): A representation with explicit tracking of responsibilities and desugaring. Tailored for applying optimizations.
MarcelGarus marked this conversation as resolved.
Show resolved Hide resolved
* LIR ("Low-Level Intermediate Representation"): An instruction code for a stack-based virtual machine.

Note that if an error occurs in a compilation stage, we don't immediately abort but rather just try to contain the error in a subtree of the code and emit an error node.
This means that even if you have a syntax error (missing parentheses, etc.), the tooling in other parts of the source still works – including auto-completion, edit-time evaluation, formatting, etc.
Expand Down
34 changes: 17 additions & 17 deletions compiler/src/compiler/ast_to_hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub trait AstToHir: CstDb + CstToAst {
}

fn hir_to_ast_id(db: &dyn AstToHir, id: hir::Id) -> Option<ast::Id> {
let (_, hir_to_ast_id_mapping) = db.hir(id.module.clone()).unwrap();
let (_, hir_to_ast_id_mapping) = db.hir(id.module.clone())?;
MarcelGarus marked this conversation as resolved.
Show resolved Hide resolved
hir_to_ast_id_mapping.get(&id).cloned()
}
fn hir_to_cst_id(db: &dyn AstToHir, id: hir::Id) -> Option<cst::Id> {
Expand Down Expand Up @@ -337,29 +337,29 @@ impl<'a> Context<'a> {
})) if name == "needs" => {
let expression = match &self.lower_call_arguments(&call.arguments[..])[..] {
[condition, reason] => Expression::Needs {
condition: Box::new(condition.clone()),
reason: Box::new(reason.clone()),
condition: condition.clone(),
reason: reason.clone(),
},
[condition] => Expression::Needs {
condition: Box::new(condition.clone()),
reason: Box::new(self.push(
condition: condition.clone(),
reason: self.push(
None,
Expression::Text(
match self.db.ast_id_to_span(call.arguments[0].id.clone()) {
Some(span) => format!(
"`{}` was not satisfied",
&self
.db
.get_module_content_as_string(
call.arguments[0].id.module.clone()
)
.unwrap()[span],
),
"`{}` was not satisfied",
&self
.db
.get_module_content_as_string(
call.arguments[0].id.module.clone()
)
.unwrap()[span],
),
None => "the needs of a function were not met".to_string(),
},
),
None,
)),
),
},
_ => {
return self.push_error(
Expand Down Expand Up @@ -459,7 +459,7 @@ impl<'a> Context<'a> {

impl<'a> Context<'a> {
fn generate_sparkles(&mut self) {
let mut sparkles_map = im::HashMap::new();
let mut sparkles_map = HashMap::new();

for builtin_function in builtin_functions::VALUES.iter() {
let symbol = self.push(
Expand Down Expand Up @@ -505,7 +505,7 @@ impl<'a> Context<'a> {
Expression::Lambda(Lambda {
parameters: vec![relative_path],
body: inner_body,
fuzzable: false,
fuzzable: true,
MarcelGarus marked this conversation as resolved.
Show resolved Hide resolved
}),
Some("use".to_string()),
);
Expand All @@ -517,7 +517,7 @@ impl<'a> Context<'a> {
// HirId(~:test.candy:100): HirId(~:test.candy:101),
// ]

let mut exports = im::HashMap::new();
let mut exports = HashMap::new();
for (name, id) in self.public_identifiers.clone() {
exports.insert(
self.push(
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/compiler/cst_to_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn ast(db: &dyn CstToAst, module: Module) -> Option<AstResult> {
let cst = cst.unwrap_whitespace_and_comment();
context.lower_csts(&cst)
}
Err(InvalidModuleError::DoesNotExist) => return None,
Err(InvalidModuleError::DoesNotExist | InvalidModuleError::IsToolingModule) => return None,
Err(InvalidModuleError::InvalidUtf8) => {
vec![Ast {
id: context.create_next_id_without_mapping(),
Expand Down
83 changes: 82 additions & 1 deletion compiler/src/compiler/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{ast::AstError, hir::HirError, rcst::RcstError};
use crate::module::Module;
use std::ops::Range;
use std::{fmt::Display, ops::Range};

#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct CompilerError {
Expand All @@ -16,3 +16,84 @@ pub enum CompilerErrorPayload {
Ast(AstError),
Hir(HirError),
}

impl Display for CompilerErrorPayload {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match self {
CompilerErrorPayload::InvalidUtf8 => "The module contains invalid UTF-8.".to_string(),
CompilerErrorPayload::Rcst(error) => match error {
RcstError::CurlyBraceNotClosed => "The curly brace is not closed.",
RcstError::IdentifierContainsNonAlphanumericAscii => {
"This identifier contains non-alphanumeric ASCII characters."
}
RcstError::IntContainsNonDigits => {
"This integer contains characters that are not digits."
}
RcstError::ListItemMissesValue => "This list item is missing a value.",
RcstError::ListNotClosed => "The list is not closed.",
RcstError::OpeningParenthesisWithoutExpression => {
"Here's an opening parenthesis without an expression after it."
}
RcstError::ParenthesisNotClosed => "This parenthesis isn't closed.",
RcstError::PipeMissesCall => "There should be a call after this pipe.",
RcstError::StructFieldMissesColon => "This struct field misses a colon.",
RcstError::StructFieldMissesKey => "This struct field misses a key.",
RcstError::StructFieldMissesValue => "This struct field misses a value.",
RcstError::StructNotClosed => "This struct is not closed.",
RcstError::SymbolContainsNonAlphanumericAscii => {
"This symbol contains non-alphanumeric ASCII characters."
}
RcstError::TextNotClosed => "This text isn't closed.",
RcstError::TextNotSufficientlyIndented => "This text isn't sufficiently indented.",
RcstError::TooMuchWhitespace => "There is too much whitespace here.",
RcstError::UnexpectedCharacters => "This is an unexpected character.",
RcstError::UnparsedRest => "The parser couldn't parse this rest.",
RcstError::WeirdWhitespace => "This is weird whitespace.",
RcstError::WeirdWhitespaceInIndentation => {
"This is weird whitespace. Make sure to use indent using two spaces."
}
}
.to_string(),
CompilerErrorPayload::Ast(error) => match error {
AstError::ExpectedParameter => "A parameter should come here.",
AstError::LambdaWithoutClosingCurlyBrace => {
"This lambda doesn't have a closing curly brace."
}
AstError::ListItemWithoutComma => "This list item should be followed by a comma.",
AstError::ListWithNonListItem => "This is not a list item.",
AstError::ListWithoutClosingParenthesis => {
"This list doesn't have a closing parenthesis."
}
AstError::ParenthesizedWithoutClosingParenthesis => {
"This expression is parenthesized, but the closing parenthesis is missing."
}
AstError::StructKeyWithoutColon => "This struct key should be followed by a colon.",
AstError::StructValueWithoutComma => {
"This struct value should be followed by a comma."
}
AstError::StructWithNonStructField => "Structs should only contain struct key.",
AstError::StructWithoutClosingBrace => {
"This struct doesn't have a closing bracket."
}
AstError::TextWithoutClosingQuote => "This text never ends.",
AstError::UnexpectedPunctuation => "This punctuation was unexpected.",
}
.to_string(),
CompilerErrorPayload::Hir(error) => match error {
HirError::NeedsWithWrongNumberOfArguments { num_args } => {
format!("This `needs` is given {num_args} arguments, but it needs one or two – a condition and an optional reason.")
MarcelGarus marked this conversation as resolved.
Show resolved Hide resolved
}
HirError::PublicAssignmentInNotTopLevel => {
"This re-assigns a value that is public. That's not allowed.".to_string()
}
HirError::PublicAssignmentWithSameName { name } => {
format!("There already exists a public assignment named `{name}`.")
}
HirError::UnknownReference { name } => {
format!("Here, you reference `{name}`, but that name is not in scope.")
}
},
MarcelGarus marked this conversation as resolved.
Show resolved Hide resolved
};
write!(f, "{message}")
}
}
87 changes: 54 additions & 33 deletions compiler/src/compiler/hir.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use super::{ast_to_hir::AstToHir, error::CompilerError};
use crate::{builtin_functions::BuiltinFunction, module::Module};
use im::HashMap;
use crate::{
builtin_functions::BuiltinFunction,
module::{Module, ModuleKind, Package},
};
use itertools::Itertools;
use linked_hash_map::LinkedHashMap;
use num_bigint::BigUint;
use std::{
collections::HashSet,
collections::HashMap,
fmt::{self, Display, Formatter},
hash,
sync::Arc,
};
use tracing::info;
Expand Down Expand Up @@ -86,8 +89,8 @@ impl Expression {
}
Expression::Builtin(_) => {}
Expression::Needs { condition, reason } => {
ids.push(*condition.clone());
ids.push(*reason.clone());
ids.push(condition.clone());
ids.push(reason.clone());
}
Expression::Error { .. } => {}
}
Expand All @@ -112,6 +115,36 @@ impl Id {
Self { module, keys }
}

/// An ID that can be used to blame the tooling. For example, when calling
/// the `main` function, we want to be able to blame the platform for
/// passing a wrong environment.
fn tooling(name: String) -> Self {
Self {
module: Module {
package: Package::Tooling(name),
path: vec![],
kind: ModuleKind::Code,
},
keys: vec![],
}
}
pub fn platform() -> Self {
Self::tooling("platform".to_string())
}
pub fn fuzzer() -> Self {
Self::tooling("fuzzer".to_string())
}
/// TODO: Currently, when a higher-order function calls a closure passed as
/// a parameter, that's registered as a normal call instruction, making the
/// callsite in the higher-order function responsible for the successful
/// fulfillment of the passed function's `needs`. We probably want to change
/// how that works so that the caller of the higher-order function is at
/// fault when passing a panicking function. After we did that, we should be
/// able to remove this ID.
pub fn complicated_responsibility() -> Self {
Self::tooling("complicated-responsibility".to_string())
}

pub fn is_root(&self) -> bool {
self.keys.is_empty()
}
Expand All @@ -138,7 +171,7 @@ impl Display for Id {
}
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Expression {
Int(BigUint),
Text(String),
Expand All @@ -157,8 +190,8 @@ pub enum Expression {
relative_path: Id,
},
Needs {
condition: Box<Id>,
reason: Box<Id>,
condition: Id,
reason: Id,
},
Error {
child: Option<Id>,
Expand All @@ -170,41 +203,29 @@ impl Expression {
Expression::Symbol("Nothing".to_string())
}
}
#[allow(clippy::derive_hash_xor_eq)]
impl hash::Hash for Expression {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Lambda {
pub parameters: Vec<Id>,
pub body: Body,
pub fuzzable: bool,
}
impl Lambda {
pub fn captured_ids(&self, my_id: &Id) -> Vec<Id> {
let mut captured = vec![];
self.body.collect_all_ids(&mut captured);
captured
.into_iter()
.filter(|potentially_captured_id| {
!my_id.is_same_module_and_any_parent_of(potentially_captured_id)
})
.collect::<HashSet<_>>()
.into_iter()
.collect_vec()
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Body {
pub expressions: LinkedHashMap<Id, Expression>,
pub identifiers: HashMap<Id, String>,
}
impl Body {
#[allow(dead_code)]
pub fn return_value(&self) -> Id {
self.expressions
.keys()
.last()
.expect("no expressions")
.clone()
#[allow(clippy::derive_hash_xor_eq)]
impl hash::Hash for Body {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.expressions.hash(state);
}
}

Expand Down Expand Up @@ -329,7 +350,7 @@ impl fmt::Display for Lambda {
}
impl fmt::Display for Body {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (id, expression) in self.expressions.iter() {
for (id, expression) in &self.expressions {
writeln!(f, "{id} = {expression}")?;
}
Ok(())
Expand All @@ -355,7 +376,7 @@ impl Expression {
}
}
impl Body {
fn find(&self, id: &Id) -> Option<&Expression> {
pub fn find(&self, id: &Id) -> Option<&Expression> {
if let Some(expression) = self.expressions.get(id) {
Some(expression)
} else {
Expand Down
Loading