tis_100/
core.rs

1//! Basic types for parsing and interpreting TIS-100 assembly code.
2
3use std::str::FromStr;
4
5/// A TIS-100 port.
6#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
7pub enum Port {
8    UP,
9    DOWN,
10    LEFT,
11    RIGHT,
12}
13
14use self::Port::*;
15
16/// An error which can be returned when parsing a port.
17#[derive(Debug, PartialEq)]
18pub struct ParsePortError;
19
20impl FromStr for Port {
21    type Err = ParsePortError;
22
23    fn from_str(s: &str) -> Result<Self, Self::Err> {
24        match s {
25            "UP" => Ok(UP),
26            "DOWN" => Ok(DOWN),
27            "LEFT" => Ok(LEFT),
28            "RIGHT" => Ok(RIGHT),
29            _ => Err(ParsePortError)
30        }
31    }
32}
33
34/// Get the opposing direction for a given port.
35///
36/// # Example
37///
38/// ```
39/// use tis_100::core::Port::*;
40/// use tis_100::core::opposite_port;
41///
42/// assert_eq!(opposite_port(UP), DOWN);
43/// assert_eq!(opposite_port(LEFT), RIGHT);
44/// ```
45pub fn opposite_port(port: Port) -> Port {
46    match port {
47        UP => DOWN,
48        DOWN => UP,
49        LEFT => RIGHT,
50        RIGHT => LEFT,
51    }
52}
53
54/// A TIS-100 port or pseudo-port.
55#[derive(Debug, PartialEq, Copy, Clone)]
56pub enum IoRegister {
57    DIR(Port),
58    ANY, // All-caps so that we don't conflict with std::any::Any.
59    LAST,
60}
61
62use self::IoRegister::*;
63
64/// An error which can be returned when parsing an IO register.
65#[derive(Debug, PartialEq)]
66pub struct ParseIoRegisterError;
67
68impl FromStr for IoRegister {
69    type Err = ParseIoRegisterError;
70
71    fn from_str(s: &str) -> Result<Self, Self::Err> {
72        match s {
73            "ANY" => Ok(ANY),
74            "LAST" => Ok(LAST),
75            _ => if let Ok(port) = str::parse::<Port>(s) {
76                Ok(DIR(port))
77            } else {
78                Err(ParseIoRegisterError)
79            }
80        }
81    }
82}
83
84/// A TIS-100 register.
85#[derive(Debug, PartialEq, Copy, Clone)]
86pub enum Register {
87    ACC,
88    NIL,
89    IO(IoRegister),
90}
91
92use self::Register::*;
93
94/// An error which can be returned when parsing a register.
95#[derive(Debug, PartialEq)]
96pub struct ParseRegisterError;
97
98impl FromStr for Register {
99    type Err = ParseRegisterError;
100
101    fn from_str(s: &str) -> Result<Self, Self::Err> {
102        match s {
103            "ACC" => Ok(ACC),
104            "NIL" => Ok(NIL),
105            _ => {
106                if let Ok(reg) = str::parse::<IoRegister>(s) {
107                    Ok(IO(reg))
108                } else {
109                    Err(ParseRegisterError)
110                }
111            }
112        }
113    }
114}
115
116/// The source component of a TIS-100 instruction.
117#[derive(Debug, PartialEq, Copy, Clone)]
118pub enum Source {
119    VAL(isize),
120    REG(Register),
121}
122
123use self::Source::*;
124
125/// An error which can be returned when parsing a source.
126#[derive(Debug, PartialEq)]
127pub struct ParseSourceError;
128
129impl FromStr for Source {
130    type Err = ParseSourceError;
131
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        if let Ok(val) = str::parse::<isize>(s) {
134            Ok(VAL(val))
135        } else if let Ok(register) = str::parse::<Register>(s) {
136            Ok(REG(register))
137        } else {
138            Err(ParseSourceError)
139        }
140    }
141}
142
143/// A valid TIS-100 instruction.
144#[derive(Debug, PartialEq, Copy, Clone)]
145pub enum Instruction {
146    Nop,
147    Mov(Source, Register),
148    Swp,
149    Sav,
150    Add(Source),
151    Sub(Source),
152    Neg,
153    Jmp(isize),
154    Jez(isize),
155    Jnz(isize),
156    Jgz(isize),
157    Jlz(isize),
158    Jro(Source),
159}
160
161/// The list of instructions created by parsing the program source code. The
162/// instructions can then be evaluated by a basic execution node.
163pub type Program = Vec<Instruction>;
164
165#[test]
166fn test_parse_port() {
167    assert_eq!(str::parse::<Port>("UP"), Ok(UP));
168    assert_eq!(str::parse::<Port>("DOWN"), Ok(DOWN));
169    assert_eq!(str::parse::<Port>("LEFT"), Ok(LEFT));
170    assert_eq!(str::parse::<Port>("RIGHT"), Ok(RIGHT));
171    assert_eq!(str::parse::<Port>("up"), Err(ParsePortError));
172    assert_eq!(str::parse::<Port>("bad"), Err(ParsePortError));
173}
174
175#[test]
176fn test_parse_io_register() {
177    assert_eq!(str::parse::<IoRegister>("UP"), Ok(DIR(UP)));
178    assert_eq!(str::parse::<IoRegister>("ANY"), Ok(ANY));
179    assert_eq!(str::parse::<IoRegister>("LAST"), Ok(LAST));
180    assert_eq!(str::parse::<IoRegister>("any"), Err(ParseIoRegisterError));
181    assert_eq!(str::parse::<IoRegister>("bad"), Err(ParseIoRegisterError));
182}
183
184#[test]
185fn test_parse_register() {
186    assert_eq!(str::parse::<Register>("ACC"), Ok(ACC));
187    assert_eq!(str::parse::<Register>("NIL"), Ok(NIL));
188    assert_eq!(str::parse::<Register>("UP"), Ok(IO(DIR(UP))));
189    assert_eq!(str::parse::<Register>("acc"), Err(ParseRegisterError));
190    assert_eq!(str::parse::<Register>("bad"), Err(ParseRegisterError));
191}
192
193#[test]
194fn test_parse_source() {
195    assert_eq!(str::parse::<Source>("ACC"), Ok(REG(ACC)));
196    assert_eq!(str::parse::<Source>("1"), Ok(VAL(1)));
197    assert_eq!(str::parse::<Source>("bad"), Err(ParseSourceError));
198}