mycon/program/ip/mod.rs
1// Copyright 2018 Johannes M. Griebler
2//
3// This file is part of mycon.
4//
5// mycon is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// mycon is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with mycon. If not, see <https://www.gnu.org/licenses/>.
17
18//! A single instruction pointer in a running program.
19
20mod instruction;
21
22use config::Trace;
23use data::{Value, Point, Delta};
24use data::space::Space;
25use data::stack::StackStack;
26use super::Context;
27
28/// An instruction pointer in a running program.
29#[derive(Clone)]
30pub(super) struct Ip {
31 id: Value,
32 position: Point,
33 delta: Delta,
34 storage: Point,
35 stacks: StackStack,
36 string: bool,
37}
38
39impl Ip {
40 /// Creates a new `Ip` at the origin, facing east.
41 ///
42 /// The `Ip` will be in the configuration it should have at program start:
43 /// Its [`Delta`] will be `(1, 0)`, its [`StackStack`] will contain a single
44 /// empty stack.
45 ///
46 /// [`Delta`]: ../../data/struct.Delta.html
47 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
48 pub(super) fn new() -> Ip {
49 Ip {
50 id: 0,
51 position: Point { x: 0, y: 0 },
52 delta: Delta { dx: 1, dy: 0 },
53 storage: Point { x: 0, y: 0 },
54 stacks: StackStack::new(),
55 string: false,
56 }
57 }
58
59 /// Returns the [`Value`] at the `Ip`'s current position.
60 ///
61 /// [`Value`]: ../../data/struct.Value.html
62 fn get_current(&self, space: &Space) -> Value {
63 space.get(self.position)
64 }
65
66 /// Sets `Ip`'s identifier.
67 pub(super) fn set_id(&mut self, id: Value) {
68 self.id = id;
69 }
70
71 /// Executes a single command and moves the `Ip` to the next.
72 pub(super) fn tick(&mut self, ctx: &mut Context) {
73 if !self.string {
74 self.find_command(&ctx.space);
75 }
76
77 let v = self.get_current(&ctx.space);
78
79 if self.string {
80 if v == 34 {
81 self.string = false;
82 self.step(&ctx.space);
83 self.find_command(&ctx.space);
84 } else {
85 self.push(v);
86 self.step(&ctx.space);
87
88 if v == 32 {
89 self.skip_space(&ctx.space);
90 }
91 }
92
93 return;
94 }
95
96 if let Some(c) = ::std::char::from_u32(v as u32) {
97 self.execute(ctx, c);
98 } else {
99 self.reflect();
100 }
101
102 self.step(&ctx.space);
103
104 if !self.string {
105 self.find_command(&ctx.space);
106 }
107 }
108
109 /// Advances the `Ip`'s position by one step of its current [`Delta`].
110 ///
111 /// [`Delta`]: ../../data/struct.Delta.html
112 fn step(&mut self, space: &Space) {
113 self.position = space.new_position(self.position, self.delta);
114 }
115
116 /// Executes a single command, without moving the `Ip`'s afterwards.
117 fn execute(&mut self, ctx: &mut Context, command: char) {
118 match command {
119 ' ' => panic!("attempted to execute ' '"),
120 '!' => self.negate(),
121 '"' => self.string_mode(),
122 '#' => self.trampoline(ctx),
123 '$' => self.discard(),
124 '%' => self.rem(),
125 '&' => self.input_decimal(ctx),
126 '\'' => self.fetch_char(ctx),
127 '(' => self.load_semantics(),
128 ')' => self.unload_semantics(),
129 '*' => self.mul(),
130 '+' => self.add(),
131 ',' => self.output_char(ctx),
132 '-' => self.sub(),
133 '.' => self.output_decimal(ctx),
134 '/' => self.div(),
135 '0' => self.push_zero(),
136 '1' => self.push_one(),
137 '2' => self.push_two(),
138 '3' => self.push_three(),
139 '4' => self.push_four(),
140 '5' => self.push_five(),
141 '6' => self.push_six(),
142 '7' => self.push_seven(),
143 '8' => self.push_eight(),
144 '9' => self.push_nine(),
145 ':' => self.duplicate(),
146 ';' => panic!("attempted to execute ';'"),
147 '<' => self.go_west(),
148 '=' => self.system_execute(ctx),
149 '>' => self.go_east(),
150 '?' => self.randomize_delta(),
151 '@' => self.stop(ctx),
152 'A' ... 'Z' => self.reflect(), // TODO implement
153 '[' => self.turn_left(),
154 '\\' => self.swap(),
155 ']' => self.turn_right(),
156 '^' => self.go_north(),
157 '_' => self.if_east_west(),
158 '`' => self.greater_than(),
159 'a' => self.push_ten(),
160 'b' => self.push_eleven(),
161 'c' => self.push_twelve(),
162 'd' => self.push_thirteen(),
163 'e' => self.push_fourteen(),
164 'f' => self.push_fifteen(),
165 'g' => self.get(ctx),
166 'h' => self.reflect(),
167 'i' => self.read_file(ctx),
168 'j' => self.jump(ctx),
169 'k' => self.iterate(ctx),
170 'l' => self.reflect(),
171 'm' => self.reflect(),
172 'n' => self.clear(),
173 'o' => self.write_file(ctx),
174 'p' => self.put(ctx),
175 'q' => self.terminate(ctx),
176 'r' => self.reflect(),
177 's' => self.store_char(ctx),
178 't' => self.split(ctx),
179 'u' => self.dig(),
180 'v' => self.go_south(),
181 'w' => self.compare(),
182 'x' => self.absolute_delta(),
183 'y' => self.get_sysinfo(ctx),
184 'z' => (),
185 '{' => self.begin_block(),
186 '|' => self.if_north_south(),
187 '}' => self.end_block(),
188 '~' => self.input_char(ctx),
189 _ => self.reflect(),
190 }
191
192 ctx.config.do_trace(Trace::new(self.id, command, self.position, &self.stacks));
193 }
194
195 /// Sets the `Ip`'s [`Delta`] to a new value.
196 ///
197 /// [`Delta`]: ../../data/struct.Delta.html
198 fn set_delta(&mut self, delta: Delta) {
199 self.delta = delta;
200 }
201
202 /// Pushes a [`Value`] to the `Ip`'s [`StackStack`].
203 ///
204 /// [`Value`]: ../../data/struct.Value.html
205 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
206 fn push(&mut self, value: Value) {
207 self.stacks.push(value);
208 }
209
210 /// Pushes a string on the `Ip`'s [`StackStack`].
211 ///
212 /// Returns the number of cells that were pushed.
213 ///
214 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
215 fn push_string(&mut self, s: &str) -> usize {
216 self.stacks.push_string(s)
217 }
218
219 /// Pops the top [`Value`] off the `Ip`'s [`StackStack`].
220 ///
221 /// [`Value`]: ../../data/struct.Value.html
222 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
223 fn pop(&mut self) -> Value {
224 self.stacks.pop()
225 }
226
227 /// Pops a string off the `Ip`'s [`StackStack`].
228 ///
229 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
230 fn pop_string(&mut self) -> Option<String> {
231 self.stacks.pop_string()
232 }
233
234 /// Advances the `Ip`'s position to the next command in its path.
235 ///
236 /// Any intervening empty space or areas delimited by semicolons will be
237 /// skipped.
238 pub(super) fn find_command(&mut self, space: &Space) {
239 let mut skip = false;
240
241 loop {
242 match self.get_current(space) {
243 32 => (),
244 59 => skip = !skip,
245 _ if skip => (),
246 _ => return,
247 }
248
249 self.step(space);
250 }
251 }
252
253 /// Finds the next command in the `Ip`'s path, without moving it.
254 fn peek_command(&mut self, space: &Space) -> Value {
255 let orig_position = self.position;
256
257 self.step(space);
258 self.find_command(space);
259
260 let ret = self.get_current(space);
261
262 self.position = orig_position;
263
264 ret
265 }
266
267 /// Skips all empty space in the path of the `Ip`.
268 ///
269 /// Similar to [`find_command`], except that semicolons are treated just
270 /// like any other non-space character.
271 ///
272 /// This function will be used if the `Ip` is in string mode, in which each
273 /// encountered character will be pushed to the [`StackStack`], but any
274 /// contiguous sequence of spaces will be collapsed into one.
275 ///
276 /// [`find_command`]: #method.find_command
277 /// [`StackStack`]: ../../data/stack/struct.StackStack.html
278 fn skip_space(&mut self, space: &Space) {
279 while self.get_current(space) == 32 {
280 self.step(space);
281 }
282 }
283}