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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
use std::fs::File;
use std::io::prelude::*;
use serde_json;

use Operation;
use Value;
use Location;
use state::InternalState;
use std::fs::OpenOptions;

// JSON data format for json-ified source code
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum JsonOperand {
    Label(String),
    Address(u32),
    Cell(u32)
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JsonOperation {
    operation: String,
    operand: Option<JsonOperand>
}

// JSON data format for input files:
// using ::Value directly forces users to write horrible things,
// such as "input_tape": [{"Number": {"value": 3}}].
// Users can now write "input_tape: [3] and be happy.
#[derive(Serialize, Deserialize, Clone)]
#[serde(untagged)]
enum JsonValue {
    Number(i32),
    Character(char)
}

#[derive(Serialize, Deserialize, Clone)]
struct Config {
    input_tape: Vec<JsonValue>,
    memory: Vec<Option<JsonValue>>
}

impl JsonOperation {
    fn new(operation: String, operand: Option<JsonOperand>) -> Self {
        Self {
            operation: operation,
            operand: operand
        }
    }
}

fn to_operator(json_op: JsonOperation, labels_mapping: &Vec<(String, usize)>) -> Operation {
    if json_op.operation == String::from("inbox"){ return Operation::Inbox{}; }
    else if json_op.operation == String::from("add") {
        let cell_to_add = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'add'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::Add{cell: cell_to_add};
    }
    else if json_op.operation == String::from("sub") {
        let cell_to_sub = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'sub'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::Sub{cell: cell_to_sub};
    }
    else if json_op.operation == String::from("copyfrom") {
        let cell = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'copyfrom'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::CopyFrom{cell: cell};
    }
    else if json_op.operation == String::from("copyto") {
        let cell = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'copyto'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::CopyTo{cell: cell};
    }
    else if json_op.operation == String::from("bump+") {
        let cell = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'bump+'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::BumpPlus{cell: cell};
    }
    else if json_op.operation == String::from("bump-") {
        let cell = match json_op.operand.unwrap() {
            JsonOperand::Label(_) => panic!("only Address or Cell are valid operand for 'bump-'"),
            JsonOperand::Address(cell) => Location::Address(cell as usize),
            JsonOperand::Cell(cell) => Location::Cell(cell as usize)
        };
        return Operation::BumpMinus{cell: cell};
    }
    else if json_op.operation == String::from("label") {
        return Operation::Label{};
    }
    else if json_op.operation == String::from("jmp") {
        if let JsonOperand::Label(label_name) = json_op.operand.unwrap() {
            let next_position = position_from_label(&label_name, &labels_mapping).unwrap();
            return Operation::Jump{next_operation: next_position};
        }
        else {
            panic!("only Labels are valid operands for jmp");
        }
    }
    else if json_op.operation == String::from("jneg") {
        if let JsonOperand::Label(label_name) = json_op.operand.unwrap() {
            let next_position = position_from_label(&label_name, &labels_mapping).unwrap();
            return Operation::JumpNegative{next_operation: next_position};
        }
        else {
            panic!("only Labels are valid operands for jneg");
        }
    }
    else if json_op.operation == String::from("jez") {
        if let JsonOperand::Label(label_name) = json_op.operand.unwrap() {
            let next_position = position_from_label(&label_name, &labels_mapping).unwrap();
            return Operation::JumpEqualsZero{next_operation: next_position};
        }
        else {
            panic!("only Labels are valid operands for jez");
        }
    }
    else if json_op.operation == String::from("outbox") { return Operation::Outbox{}; }
    else { panic!(format!("unrecognized operation {}", json_op.operation)) }
}

fn labels_to_positions(source_code: &Vec<JsonOperation>) -> Vec<(String, usize)> {
    let mut labels : Vec<(String, usize)> = vec!();

    for (index, operation) in source_code.iter().enumerate() {
        if operation.operation == String::from("label") {
            if let JsonOperand::Label(label_name) = operation.clone().operand.unwrap() {
                labels.push((label_name, index));
            }
        }
    }

   return labels;
}

fn position_from_label(label: &String, mapping: &Vec<(String, usize)>) -> Option<usize> {
    mapping.iter().filter_map(|pair| {
        let &(ref candidate_label, position) = pair;
        if label == candidate_label {
            return Some(position);
        }
        else {
            return None
        }
    }).last()
}

pub fn read_file(srcpath: String) -> Vec<Operation> {
    let mut file = File::open(srcpath).unwrap();
    let mut contents = String::new();
    let file_read_ok = file.read_to_string(&mut contents);
    if file_read_ok.is_err() {
        panic!("could not read the file!");
    }

    read_instructions(contents)
}

pub fn read_instructions(serialized_code: String) -> Vec<Operation> {
    let source_code: Vec<JsonOperation> = serde_json::from_str(&serialized_code).unwrap();
    let position_for_label = labels_to_positions(&source_code);
    let mut res: Vec<Operation> = vec!();
    for json_op in source_code {
        res.push(to_operator(json_op.clone(), &position_for_label));
    }

    return res;
}

pub fn read_config(path: String) -> InternalState  {
    let mut file = File::open(path).unwrap();
    let mut contents = String::new();
    let file_read_ok = file.read_to_string(&mut contents);
    if file_read_ok.is_err() {
        panic!("could not read the file!");
    }

    read_config_from_string(contents)
}

pub fn read_config_from_string(serialized_input: String) -> InternalState {
    let input_config: Config = serde_json::from_str(&serialized_input).unwrap();
    return InternalState::new(None, 0)
        .with_input_tape(input_config.input_tape.into_iter().map(|input| match input {
            JsonValue::Number(num_) => Value::Number { value: num_ },
            JsonValue::Character(char_) => Value::Character { value: char_ }
        }).collect())
        .with_memory(input_config.memory.into_iter().map(|memory_value| match memory_value {
            Some(JsonValue::Number(num_)) => Some(Value::Number { value: num_ }),
            Some(JsonValue::Character(char_)) => Some(Value::Character { value: char_ }),
            None => None
        }).collect())
        .clone();
}

#[derive(Serialize)]
pub struct StateDump {
    internal_state: InternalState,
    ended_with_error: bool,
    error_reason: String
}

pub fn serialize_state(internal_state: &InternalState, error_reason: &String) -> String {
    let state_dump = StateDump {
        internal_state: internal_state.clone(),
        ended_with_error: !error_reason.is_empty(),
        error_reason: error_reason.to_string(),
    };

    serde_json::to_string(&state_dump).unwrap()
}

pub fn dump_state(internal_state: &InternalState, srcpath: &str, error_reason: &String) {
    let state_dump = StateDump {
        internal_state: internal_state.clone(),
        ended_with_error: !error_reason.is_empty(),
        error_reason: error_reason.to_string()
    };

    let raw_state = serde_json::to_string(&state_dump).unwrap();

    let result = OpenOptions::new()
        .append(true).create(true).truncate(false)
        .open(srcpath);
    if result.is_ok() {
        let mut file = result.unwrap();
        file.write(raw_state.as_bytes());
        // hrm-proxy expects a single line for each execution state --> add line separator
        file.write(b"\n");
    }
    else {
        panic!("cannot write the file?!?!?! {:?}", result.err());
    }
}

#[cfg(test)]
mod test {
    use Operation;
    use json::to_operator;
    use json::JsonOperation;
    use json::JsonOperand;
    use json::labels_to_positions;

    #[test]
    #[should_panic]
    fn to_operator_unknown() {
        let empty_labels_mapping = vec!();
        let src = JsonOperation{
            operation: String::from("fsdfsadfsadjsdf"),
            operand: None
        };

        to_operator(src, &empty_labels_mapping);
    }

    #[test]
    fn to_operator_label() {
        let empty_labels_mapping = vec!();
        let src = JsonOperation{
            operation: String::from("label"),
            operand: Some(JsonOperand::Label(String::from("mylabel")))
        };
        let result = to_operator(src, &empty_labels_mapping);

        assert!(match result {
            Operation::Label => true,
            _ => false
        });
    }

    #[test]
    fn to_operator_jump_label_found() {
        let mapping = vec!((String::from("myLabel"), 3));
        let operation = JsonOperation{
            operation: String::from("jmp"),
            operand: Some(JsonOperand::Label(String::from("myLabel")))
        };

        let result = to_operator(operation, &mapping);

        assert!(match result {
            Operation::Jump{next_operation: 3} => true,
            _ => false
        });
    }

    #[test]
    #[should_panic]
    fn to_operator_jump_label_not_found() {
        let mapping = vec!((String::from("myLabel"), 3));
        let operation = JsonOperation{
            operation: String::from("jmp"),
            operand: Some(JsonOperand::Label(String::from("fdfsdfsadj")))
        };

        to_operator(operation, &mapping);
    }

    #[test]
    fn labels_to_positions_empty_code() {
        let empty_vec = vec!();
        let result = labels_to_positions(&empty_vec);
        assert!(result.len() == 0);
    }

    #[test]
    fn labels_to_positions_no_labels() {
        let operations = vec!(
            JsonOperation{
                operation: String::from("copyto"),
                operand: Some(JsonOperand::Cell(2))
        });
        let result = labels_to_positions(&operations);
        assert!(result.len() == 0);
    }

    #[test]
    fn labels_to_positions_with_labels() {
        let operations = vec!(
            JsonOperation::new(String::from("label"), Some(JsonOperand::Label(String::from("firstlabel")))),
            JsonOperation::new(String::from("inbox"), None),
            JsonOperation::new(String::from("label"), Some(JsonOperand::Label(String::from("secondlabel")))),
            JsonOperation::new(String::from("jmp"), Some(JsonOperand::Label(String::from("firstlabel"))))
        );

        let result = labels_to_positions(&operations);

        assert!(result.len() == 2);
        assert!(result[0] == (String::from("firstlabel"), 0));
        assert!(result[1] == (String::from("secondlabel"), 2));
    }
}