1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use super::facts::Facts;
use crate::{Environment, FunctionId};

/// For anything that might involve a call, including gets, sets and actual calls
pub(crate) trait CallCheckingBehavior {
	// TODO
	const CHECK_PARAMETERS: bool;

	fn get_top_level_facts<'a>(&'a mut self, environment: &'a mut Environment) -> &'a mut Facts;

	fn in_recursive_cycle(&self, function_id: FunctionId) -> bool;

	fn new_function_target<T>(
		&mut self,
		function_id: FunctionId,
		cb: impl for<'a> FnOnce(&'a mut Target) -> T,
	) -> T;
}

pub struct CheckThings;

impl CallCheckingBehavior for CheckThings {
	const CHECK_PARAMETERS: bool = true;

	fn get_top_level_facts<'a>(&'a mut self, environment: &'a mut Environment) -> &'a mut Facts {
		&mut environment.facts
	}

	fn in_recursive_cycle(&self, function_id: FunctionId) -> bool {
		// cannot get in a loop from checking
		false
	}

	fn new_function_target<T>(
		&mut self,
		function_id: FunctionId,
		cb: impl for<'a> FnOnce(&'a mut Target) -> T,
	) -> T {
		let mut target = Target(vec![TargetKind::Function(function_id)]);
		cb(&mut target)
	}
}

pub(crate) struct Target(Vec<TargetKind>);
//  {
// 	// facts: Facts,
// 	// function_id: Vec<FunctionId>,
// }

pub(crate) enum TargetKind {
	Conditional(Facts),
	Function(FunctionId),
}

impl CallCheckingBehavior for Target {
	const CHECK_PARAMETERS: bool = false;

	fn get_top_level_facts<'b>(&'b mut self, environment: &'b mut Environment) -> &'b mut Facts {
		self.0
			.iter_mut()
			.rev()
			.find_map(
				|kind| if let TargetKind::Conditional(facts) = kind { Some(facts) } else { None },
			)
			.unwrap_or(&mut environment.facts)
	}

	fn in_recursive_cycle(&self, function_id: FunctionId) -> bool {
		self.0.iter().any(|kind| matches!(kind, TargetKind::Function(id) if function_id == *id))
	}

	fn new_function_target<T>(
		&mut self,
		function_id: FunctionId,
		cb: impl for<'a> FnOnce(&'a mut Target) -> T,
	) -> T {
		self.0.push(TargetKind::Function(function_id));
		let value = cb(self);
		self.0.pop();
		value
	}
}

impl Target {
	pub(crate) fn new_conditional_target(
		&mut self,
		cb: impl for<'a> FnOnce(&'a mut Target),
	) -> Facts {
		self.0.push(TargetKind::Conditional(Facts::default()));
		cb(self);
		if let Some(TargetKind::Conditional(facts)) = self.0.pop() {
			facts
		} else {
			unreachable!()
		}
	}
}