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}