1use std::rc::Rc;
4
5use nifty_check::hir::{LocalId, Place, PlaceRoot, PlaceStep, StateId};
6use nifty_download::{StatePlace, StateReader};
7use nifty_runtime::Step;
8use nifty_syntax::Span;
9
10use super::{Frame, Machine, Result, Slot, item, out_of_range, position};
11use crate::value::Value;
12
13#[derive(Clone, Debug, PartialEq)]
15pub(crate) struct Location {
16 root: Root,
17 path: Vec<Step>,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq)]
21enum Root {
22 State(StateId),
23 Local(LocalId),
24}
25
26impl Location {
27 pub(crate) fn is_in_state(&self) -> bool {
29 matches!(self.root, Root::State(_))
30 }
31
32 pub(crate) fn item(&self, index: usize) -> Self {
34 let mut path = self.path.clone();
35 path.push(Step::Index(index));
36 Self {
37 root: self.root,
38 path,
39 }
40 }
41}
42
43impl StatePlace for Location {
44 fn steps(&self) -> &[Step] {
45 &self.path
46 }
47
48 fn with_steps(&self, steps: Vec<Step>) -> Self {
49 Self {
50 root: self.root,
51 path: steps,
52 }
53 }
54
55 fn same_root(&self, other: &Self) -> bool {
56 self.root == other.root
57 }
58}
59
60impl StateReader for Machine {
62 type Place = Location;
63
64 fn list_len(&self, place: &Location) -> Option<usize> {
65 match self.read_in_state(place)? {
66 Value::List(items) => Some(items.len()),
67 _ => None,
68 }
69 }
70
71 fn holds_text(&self, place: &Location, text: &str) -> bool {
72 matches!(self.read_in_state(place), Some(Value::String(held)) if *held == *text)
73 }
74}
75
76impl Machine {
77 fn read_in_state(&self, place: &Location) -> Option<Value> {
79 let Root::State(state) = place.root else {
80 return None;
81 };
82 walk(&self.states.borrow()[state.0], &place.path, Span::default()).ok()
83 }
84
85 pub(crate) fn locate(&self, place: &Place, frame: &mut Frame) -> Result<Location> {
87 let mut location = match place.root {
88 PlaceRoot::State(state) => Location {
89 root: Root::State(state),
90 path: Vec::new(),
91 },
92 PlaceRoot::Local(local) => match &frame[local.0] {
93 Slot::Alias(location) => location.clone(),
94 Slot::Value(_) => Location {
95 root: Root::Local(local),
96 path: Vec::new(),
97 },
98 Slot::Empty => panic!("the checker only allows using locals after they are set"),
99 },
100 };
101 for step in &place.path {
102 location.path.push(match step {
103 PlaceStep::Field(index) => Step::Field(*index),
104 PlaceStep::Index(index) => {
105 Step::Index(position(self.eval(index, frame)?.as_int(), place.span)?)
106 }
107 });
108 }
109 Ok(location)
110 }
111
112 pub(crate) fn read(&self, location: &Location, frame: &Frame, span: Span) -> Result<Value> {
113 match location.root {
114 Root::State(state) => walk(&self.states.borrow()[state.0], &location.path, span),
115 Root::Local(local) => match &frame[local.0] {
116 Slot::Value(value) => walk(value, &location.path, span),
117 _ => panic!("a local location always roots at a stored value"),
118 },
119 }
120 }
121
122 pub(crate) fn write(
123 &self,
124 location: &Location,
125 frame: &mut Frame,
126 value: Value,
127 span: Span,
128 ) -> Result<()> {
129 self.modify(location, frame, span, |target| {
130 *target = value;
131 Ok(())
132 })
133 }
134
135 pub(crate) fn write_state(&self, location: &Location, value: Value, span: Span) -> Result<()> {
140 let Root::State(state) = location.root else {
141 panic!("only a location in state can be written without its frame");
142 };
143 self.modify_state(state, &location.path, span, |target| {
144 *target = value;
145 Ok(())
146 })
147 }
148
149 pub(crate) fn modify<R>(
151 &self,
152 location: &Location,
153 frame: &mut Frame,
154 span: Span,
155 change: impl FnOnce(&mut Value) -> Result<R>,
156 ) -> Result<R> {
157 match location.root {
158 Root::State(state) => self.modify_state(state, &location.path, span, change),
159 Root::Local(local) => match &mut frame[local.0] {
160 Slot::Value(value) => change(walk_mut(value, &location.path, span)?),
161 _ => panic!("a local location always roots at a stored value"),
162 },
163 }
164 }
165
166 fn modify_state<R>(
167 &self,
168 state: StateId,
169 path: &[Step],
170 span: Span,
171 change: impl FnOnce(&mut Value) -> Result<R>,
172 ) -> Result<R> {
173 let result = {
174 let mut states = self.states.borrow_mut();
175 change(walk_mut(&mut states[state.0], path, span)?)?
176 };
177 self.changed.borrow_mut().insert(state);
178 Ok(result)
179 }
180}
181
182fn walk(value: &Value, path: &[Step], span: Span) -> Result<Value> {
183 let Some((step, rest)) = path.split_first() else {
184 return Ok(value.clone());
185 };
186 match (step, value) {
187 (Step::Field(index), Value::Struct(fields)) => walk(&fields[*index], rest, span),
188 (Step::Index(position), list) => walk(item(list, *position, span)?, rest, span),
189 (Step::Field(_), other) => panic!("checked code read a field of {other:?}"),
190 }
191}
192
193fn walk_mut<'v>(value: &'v mut Value, path: &[Step], span: Span) -> Result<&'v mut Value> {
194 let Some((step, rest)) = path.split_first() else {
195 return Ok(value);
196 };
197 match (step, value) {
198 (Step::Field(index), Value::Struct(fields)) => {
199 walk_mut(&mut Rc::make_mut(fields)[*index], rest, span)
200 }
201 (Step::Index(position), Value::List(items)) => {
202 let len = items.len();
203 let item = Rc::make_mut(items)
204 .get_mut(*position)
205 .ok_or_else(|| out_of_range(*position, len, span))?;
206 walk_mut(item, rest, span)
207 }
208 (_, other) => panic!("checked code changed part of {other:?}"),
209 }
210}