1
15
use super::error::ForthError;
2
use super::stack_machine::GasLimit;
3
use super::stack_machine::Opcode;
4
use super::stack_machine::StackMachine;
5
use std::collections::HashMap;
6
use std::convert::TryFrom;
7
use std::convert::TryInto;
8

            
9
pub use super::stack_machine::HandleTrap;
10
pub use super::stack_machine::StackMachineError;
11
pub use super::stack_machine::TrapHandled;
12
pub use super::stack_machine::TrapHandler;
13

            
14
type StackType = i16;
15
/// This Enum lists the token types that are used by the Forth interpreter
16
#[derive(Debug)]
17
pub enum Token {
18
    Number(StackType),
19
    Command(String),
20
    Constant(String),
21
    Comment(String),
22
    Colon(String),
23
    CHAR(char),
24
    Text(String),
25
    //   SDoubleTick(String),
26
    Create(String),
27
    Allocate(u8, String),
28
    SemiColon,
29
    End,
30
    Period,
31
    Error(String),
32
}
33

            
34
pub struct ForthCompiler {
35
    // This is the Stack Machine processor that runs the compiled Forth instructions
36
    pub sm: StackMachine,
37
    // These are the words that we know how to work with regardless, things like DROP, *, etc
38
    intrinsic_words: HashMap<&'static str, Opcode>,
39
    // This is where we remember where we put compiled words in the *memory* of the StackMachine
40
    // We run the interactive opcodes after these compiled words, and then erase the memory after
41
    // the compiled words again for the next batch of interactive opcodes.
42
    word_addresses: HashMap<String, usize>,
43
    // This is the location in memory that points to the location after the last compiled opcode
44
    // So its an ideal place to run interactive compiled opcodes
45
    last_function: usize,
46
}
47

            
48
impl Default for ForthCompiler {
49
    fn default() -> Self {
50
        Self::new()
51
    }
52
}
53

            
54
impl ForthCompiler {
55
56
    pub fn new() -> ForthCompiler {
56
56
        ForthCompiler {
57
56
            sm: StackMachine::new(),
58
56
            intrinsic_words: HashMap::from([
59
56
                (".", Opcode::PRINT),
60
56
                ("EMIT", Opcode::EMIT),
61
56
                ("CR", Opcode::CR),
62
56
                ("?", Opcode::QuestionMark),
63
56
                ("SPACE", Opcode::SPACE),
64
56
                ("SPACES", Opcode::SPACES),
65
56
                (".R", Opcode::RightAlign),
66
                //variable
67
                //  ("COUNT", Opcode::COUNT),
68
                //  ("TYPE", Opcode::TYPE),
69
                // stack ops
70
56
                ("DROP", Opcode::DROP),
71
56
                ("SWAP", Opcode::SWAP),
72
56
                ("OVER", Opcode::OVER),
73
56
                ("DUP", Opcode::DUP),
74
56
                ("2DROP", Opcode::TWODROP),
75
56
                ("2DUP", Opcode::TWODUP),
76
56
                ("2OVER", Opcode::TWOOVER),
77
56
                ("2SWAP", Opcode::TWOSWAP),
78
56
                ("2ROT", Opcode::TWOROT),
79
56
                ("DEPTH", Opcode::DEPTH),
80
56
                ("?DUP", Opcode::QuestionDup),
81
56
                ("ROT", Opcode::ROT),
82
                // return stack ops
83
56
                (">R", Opcode::ToR),
84
56
                ("R>", Opcode::RFrom),
85
56
                ("R@", Opcode::RFetch),
86
                // math ops
87
56
                ("NEGATE", Opcode::NEGATE),
88
56
                ("+", Opcode::ADD),
89
56
                ("-", Opcode::SUB),
90
56
                ("*", Opcode::MUL),
91
56
                ("/", Opcode::DIV),
92
56
                ("MOD", Opcode::MOD),
93
56
                ("*/", Opcode::STARSLASH),
94
56
                (">", Opcode::GT),
95
56
                ("<", Opcode::LT),
96
56
                ("=", Opcode::EQ),
97
56
                ("NOT", Opcode::NOT),
98
56
                ("AND", Opcode::AND),
99
56
                ("OR", Opcode::OR),
100
56
                ("XOR", Opcode::XOR),
101
56
                ("0<", Opcode::ZeroLess),
102
56
                ("0=", Opcode::ZeroEqual),
103
56
                ("0>", Opcode::ZeroGreater),
104
56
                ("1+", Opcode::OnePlus),
105
56
                ("1-", Opcode::OneMinus),
106
56
                ("2+", Opcode::TwoPlus),
107
56
                ("2-", Opcode::TwoMinus),
108
56
                ("LSHIFT", Opcode::LShift),
109
56
                ("RSHIFT", Opcode::RShift),
110
56
                ("TRAP", Opcode::TRAP),
111
56
                ("SETSEED", Opcode::SETSEED),
112
56
                ("RAND", Opcode::RAND),
113
                //variable ops
114
56
                ("!", Opcode::BANG),
115
56
                ("@", Opcode::AT),
116
56
                ("ALLOT", Opcode::ALLOT),
117
            ]),
118
56
            word_addresses: HashMap::new(),
119
            last_function: 0,
120
        }
121
56
    }
122
}
123

            
124
/// This Enum determines whether the Forth interpreter is in Interpreting mode or Compiling mode
125
152
#[derive(Debug, PartialEq)]
126
enum Mode {
127
    Interpreting,
128
    Compiling(String),
129
}
130

            
131
// This struct tracks information for Forth IF statements
132
#[derive(Debug)]
133
struct DeferredIfStatement {
134
    if_location: usize,
135
    else_location: Option<usize>,
136
}
137

            
138
impl DeferredIfStatement {
139
16
    pub fn new(if_location: usize) -> DeferredIfStatement {
140
16
        DeferredIfStatement {
141
            if_location,
142
16
            else_location: None,
143
        }
144
16
    }
145
}
146

            
147
struct DeferredDoStatement {
148
    do_location: usize,
149
    incr_location: usize,
150
    comparison_location: usize,
151
}
152

            
153
impl DeferredDoStatement {
154
14
    pub fn new(
155
        do_location: usize,
156
        incr_location: usize,
157
        comparison_location: usize,
158
    ) -> DeferredDoStatement {
159
14
        DeferredDoStatement {
160
            do_location,
161
            incr_location,
162
            comparison_location,
163
        }
164
14
    }
165
}
166

            
167
struct DeferredWhileStatement {
168
    begin_location: StackType,
169
    condition_location: StackType,
170
}
171

            
172
impl DeferredWhileStatement {
173
4
    pub fn new(begin_location: StackType, condition_location: StackType) -> DeferredWhileStatement {
174
4
        DeferredWhileStatement {
175
            begin_location,
176
            condition_location,
177
        }
178
4
    }
179
}
180

            
181
impl ForthCompiler {
182
    // Take a string containing Forth words and turn it into a list of Forth tokens
183
163
    fn tokenize_string(&self, s: &str) -> Result<Vec<Token>, ForthError> {
184
163
        let mut tv = Vec::new();
185
163
        let mut allocate: Vec<String> = Vec::new();
186
        //let mut is_variable = false;
187

            
188
        //  remove comments ie ( \ this is a comment )
189
163
        let mut st = Vec::<String>::new();
190
387
        for line in s.split('\n') {
191
224
            st.push(line.split("\\ ").take(1).next().unwrap_or("").to_string());
192
        }
193

            
194
163
        let st = st.join(" ");
195

            
196
163
        let s = st
197
            .split_whitespace()
198
            //.text()
199
1069
            .map(|s| s.trim())
200
1069
            .filter(|s| !s.is_empty())
201
            .collect::<Vec<_>>();
202
        // let mut string_iter = s.split_whitespace();
203

            
204
163
        let mut string_iter = s.iter();
205

            
206
        //  println!("{:?}", s);
207
        loop {
208
1115
            match string_iter.next() {
209
                // If no more text in the string, then return what we have tokenized
210
158
                None => return Ok(tv),
211
                // If we have some text to process, then process it
212
957
                Some(string_token) => {
213
                    // Try to convert it to a number
214
957
                    match string_token.parse::<StackType>() {
215
                        // We found a number, then return it as a number token
216
335
                        Ok(n) => tv.push(Token::Number(n)),
217
                        // Wasn't a number, treat it as a *word*
218
622
                        Err(_) => match string_token.to_uppercase().as_str() {
219
                            // If its a colon, create a colon token
220
622
                            ":" => match &string_iter.next() {
221
                                // If we found a token, then we need to grab the next bit of text so we know what Forth word is being compiled
222
63
                                Some(next_token) => tv.push(Token::Colon(next_token.to_string())),
223
                                // There has to be something after the colon, so this is an error since we didn't find anything
224
                                None => {
225
2
                                    return Err(ForthError::InvalidSyntax(String::from(
226
                                        "No token after :, but one is needed to compile",
227
                                    )))
228
                                }
229
                            },
230
557
                            "CHAR" => match &string_iter.next() {
231
6
                                Some(c) => match c.len() {
232
5
                                    1 => match c.chars().next() {
233
5
                                        Some(x) => tv.push(Token::CHAR(x)),
234
                                        None => todo!(),
235
                                    },
236
                                    _ => {
237
1
                                        return Err(ForthError::InvalidSyntax(String::from(
238
                                            "No single character after CHAR keyword",
239
                                        )))
240
                                    }
241
                                },
242
                                None => {
243
                                    return Err(ForthError::InvalidSyntax(String::from(
244
                                        "No single character after CHAR keyword",
245
                                    )))
246
                                }
247
                            },
248
551
                            "CONSTANT" => match &string_iter.next() {
249
4
                                Some(con) => tv.push(Token::Constant(con.to_string())),
250
                                None => {
251
1
                                    return Err(ForthError::InvalidSyntax(String::from(
252
                                        "No constant name after CONSTANT keyword",
253
                                    )));
254
                                }
255
                            },
256
546
                            "VARIABLE" | "CREATE" => match &string_iter.next() {
257
4
                                Some(var) => {
258
                                    // is_variable = true;
259
4
                                    allocate.clear();
260
4
                                    tv.push(Token::Create(var.to_string()))
261
                                }
262
                                None => {
263
1
                                    return Err(ForthError::InvalidSyntax(format!(
264
                                        "No name after {} keyword",
265
1
                                        string_token.to_uppercase()
266
                                    )));
267
                                }
268
                            },
269
                            /*
270
                            "ALLOT" => {
271
                                is_variable = false;
272
                                let amount: u8 = match allocate[0].parse() {
273
                                    Ok(x) => x,
274
                                    Err(_x) => todo!(),
275
                                };
276
                                let allocate_type = allocate[1].clone();
277
                                tv.push(Token::Allocate(amount, allocate_type.to_string()))
278
                            }
279
                            */
280
                            // Create a semicolon token
281
541
                            ";" => tv.push(Token::SemiColon),
282
                            /*
283
                            "S\"" => {
284
                                // beginning of array of chars stored in memory.
285
                                let mut text: Vec<String> = Vec::new();
286
                                loop {
287
                                    let mut mystr: String = match &string_iter.next() {
288
                                        Some(x) => x.to_string(),
289
                                        _ => todo!(),
290
                                    };
291
                                    if !mystr.ends_with('\"') {
292
                                        text.push(mystr.clone());
293
                                    } else {
294
                                        mystr.pop(); // remove " from the end of the string.
295
                                        text.push(mystr.clone());
296
                                        break;
297
                                    }
298
                                }
299
                                tv.push(Token::SDoubleTick(text.join(" ")))
300
                            }
301
                            */
302
479
                            ".\"" => {
303
                                // this is text.
304
                                // text
305
10
                                let mut text: Vec<String> = Vec::new();
306
                                loop {
307
25
                                    let mut mystr: String = match &string_iter.next() {
308
25
                                        Some(x) => x.to_string(),
309
                                        _ => todo!(),
310
                                    };
311
25
                                    if !mystr.ends_with('\"') {
312
15
                                        text.push(mystr.clone());
313
                                    } else {
314
10
                                        mystr.pop(); // remove " from the end of the string.
315
10
                                        text.push(mystr.clone());
316
                                        break;
317
                                    }
318
25
                                }
319
10
                                tv.push(Token::Text(text.join(" ")))
320
10
                            }
321
469
                            ".(" => {
322
                                // this is text.
323
                                // text
324
1
                                let mut text: Vec<String> = Vec::new();
325
                                loop {
326
2
                                    let mut mystr: String = match &string_iter.next() {
327
2
                                        Some(x) => x.to_string(),
328
                                        _ => todo!(),
329
                                    };
330
2
                                    if !mystr.ends_with(')') {
331
1
                                        text.push(mystr.clone());
332
                                    } else {
333
1
                                        mystr.pop(); // remove " from the end of the string.
334
1
                                        text.push(mystr.clone());
335
                                        break;
336
                                    }
337
2
                                }
338
1
                                tv.push(Token::Text(text.join(" ")))
339
1
                            }
340
468
                            "(" => {
341
                                // stuff between ( and ) are comments
342
2
                                let mut text: Vec<String> = Vec::new();
343
                                loop {
344
8
                                    let mut mystr: String = match &string_iter.next() {
345
8
                                        Some(x) => x.to_string(),
346
                                        _ => todo!(),
347
                                    };
348
8
                                    if !mystr.ends_with(')') {
349
6
                                        text.push(mystr.clone());
350
                                    } else {
351
2
                                        mystr.pop(); // remove " from the end of the string.
352
2
                                        text.push(mystr.clone());
353
                                        break;
354
                                    }
355
8
                                }
356
2
                                tv.push(Token::Comment(text.join(" ")))
357
2
                            }
358
466
                            "\\" => {
359
                                // rest of line comment      ie  \ this is a comment
360
                                let mut text: Vec<String> = Vec::new();
361
                                while let Some(x) = &string_iter.next() {
362
                                    text.push(x.to_string());
363
                                }
364
                                tv.push(Token::Comment(text.join(" ")))
365
                            }
366
                            // Whatever else, assume its a Forth word
367
                            _ => {
368
                                // if is_variable {
369
                                //     allocate.push(string_token.to_string());
370
                                // } else {
371
466
                                tv.push(Token::Command(string_token.to_string()))
372
                                // }
373
                            }
374
622
                        },
375
                    };
376
                }
377
            }
378
        }
379
163
    }
380

            
381
158
    fn compile_token_vector_compile_and_remove_word_definitions(
382
        &mut self,
383
        token_vector: &[Token],
384
    ) -> Result<Vec<Opcode>, ForthError> {
385
        // This is the interactive compiled token list
386
158
        let mut tvi = Vec::new();
387
        // This tracks whethere we are interpreting or compiling right now
388
158
        let mut mode = Mode::Interpreting;
389
        // This is where we start compiling the latest segment of word/interactive tokens
390
158
        let mut starting_position = 0;
391

            
392
        //println!(
393
        //    "compile_token_vector_compile_and_remove_word_definitions Compiling Forth tokens {:?}",
394
        //    token_vector
395
        //);
396
        // So, for every token we have been passed, check what it is...
397
1088
        for i in 0..token_vector.len() {
398
            // println!("{:?}", token_vector[i]);
399
936
            match &token_vector[i] {
400
                // :
401
61
                Token::Colon(s) => {
402
                    // Found Colon, so the user wants to compile a word presumably
403
61
                    match mode {
404
                        // If we are currently interpreting, then we can safely switch to compiling
405
                        Mode::Interpreting => {
406
                            // Make sure there is something to compile...
407
68
                            if i > starting_position {
408
                                // We end before the current token
409
                                // Compile whatever appeared before this compile statement
410
7
                                tvi.append(
411
7
                                    &mut self.compile_token_vector(
412
7
                                        &token_vector[starting_position..i],
413
                                    )?,
414
7
                                );
415
                            }
416
                            // Start compiling again after this token
417
61
                            starting_position = i + 1;
418
                            // Switch to compiling mode, remmeber the word we are trying to compile
419
61
                            mode = Mode::Compiling(String::from(s));
420
                        }
421
                        // We are already in compiling mode, so getting a colon is a syntax error
422
                        Mode::Compiling(_) => {
423
                            return Err(ForthError::InvalidSyntax(
424
                                "Second colon before semicolon".to_string(),
425
                            ));
426
                        }
427
                    }
428
61
                }
429
                // ;
430
                Token::SemiColon => {
431
61
                    match mode {
432
                        // We are in interpreting mode, this is a syntax error
433
                        Mode::Interpreting => {
434
1
                            return Err(ForthError::InvalidSyntax(
435
1
                                "Semicolon before colon".to_string(),
436
                            ));
437
                        }
438
                        // We have found the end of the word definition, so compile to opcodes and put into memory...
439
60
                        Mode::Compiling(s) => {
440
                            // Remove anything extraneous from the end of the opcode array (*processor memory*),
441
                            // typically previous immediate mode tokens
442
60
                            self.sm.st.opcodes.resize(self.last_function, Opcode::NOP);
443

            
444
                            // Get the compiled assembler from the token vector
445
                            // stop compiling before the ending token
446
                            let mut compiled =
447
60
                                self.compile_token_vector(&token_vector[starting_position..i])?;
448
                            // Put the return OpCode onto the end
449
55
                            compiled.push(Opcode::RET);
450
                            // The current function start is the end of the last function
451
55
                            let function_start = self.last_function;
452
                            // Move last function pointer
453
55
                            self.last_function += compiled.len();
454
                            // + the function to the opcode memory
455
55
                            self.sm.st.opcodes.append(&mut compiled);
456
                            // Remember where to find it...
457
55
                            self.word_addresses.insert(s.to_uppercase(), function_start);
458
                            // start compiling again after this token
459
55
                            starting_position = i + 1;
460
                            // Switch back to interpreting mode
461
55
                            mode = Mode::Interpreting;
462
                            //println!("Token Memory {:?}", self.sm.st.opcodes);
463
                            //println!("Word +resses {:?}", self.word_addresses);
464
                            //println!("Last function {}", self.last_function);
465
60
                        }
466
                    }
467
                }
468
                _ => (),
469
            }
470
        }
471

            
472
        // Check for an error condition and report it
473
        // If we are not in interpreting mode when we have processed all the Forth tokens, then that's an error
474
152
        if mode != Mode::Interpreting {
475
1
            return Err(ForthError::MissingSemicolonAfterColon);
476
        }
477

            
478
        // Compile any tokens that remain after processing
479
151
        let mut compiled = self.compile_token_vector(&token_vector[starting_position..])?;
480
148
        tvi.append(&mut compiled);
481
        // We need to return after running the interactive opcodes, so put the return in now
482
148
        tvi.push(Opcode::RET);
483

            
484
        // Return the interactive tokens, the compiled ones are already in memory
485
148
        Ok(tvi)
486
158
    }
487

            
488
218
    fn compile_token_vector(&mut self, token_vector: &[Token]) -> Result<Vec<Opcode>, ForthError> {
489
        // Stack of if statements, they are deferred until the THEN Forth word
490
218
        let mut deferred_if_statements = Vec::new();
491
        // used for do statements.
492
218
        let mut deferred_do_statements = Vec::new();
493
        // used for while statements
494
218
        let mut deferred_while_statements = Vec::new();
495
        // List of compiled processor opcodes that we are building up
496
218
        let mut tv: Vec<Opcode> = Vec::new();
497

            
498
        // Go through all the Forth tokens and turn them into processor Opcodes (for our StackMachine emulated processor)
499
1017
        for t in token_vector.iter() {
500
805
            match t {
501
321
                Token::Number(n) => {
502
                    // Numbers get pushed as a LDI opcode
503
321
                    tv.push(Opcode::LDI(*n));
504
                }
505
5
                Token::CHAR(c) => {
506
5
                    tv.push(Opcode::CHAR(*c));
507
                }
508
4
                Token::Constant(c) => {
509
4
                    if let std::collections::hash_map::Entry::Vacant(_e) =
510
4
                        self.sm.st.constants.entry(c.to_uppercase())
511
                    {
512
3
                        tv.push(Opcode::SETCONSTANT(c.to_uppercase()));
513
3
                    } else {
514
1
                        return Err(ForthError::ConstantAlreadyExists(c.to_string()));
515
                    }
516
4
                }
517
11
                Token::Text(t) => {
518
11
                    tv.push(Opcode::PRINTTEXT(t.to_string()));
519
                }
520
4
                Token::Create(var) => {
521
4
                    tv.push(Opcode::CREATE(var.to_string()));
522
                }
523
                Token::Allocate(size, _allocate_type) => {
524
                    tv.push(Opcode::LDI(*size as StackType));
525
                    tv.push(Opcode::ALLOT);
526
                }
527
458
                Token::Command(s) => {
528
                    // Remember where we are in the list of opcodes in case we hit a IF statement, LOOP etc...
529
458
                    let current_instruction = tv.len();
530

            
531
                    //println!("{:?}", tv);
532
458
                    match s.to_uppercase().as_ref() {
533
458
                        "BEGIN" => {
534
4
                            let begin_location = current_instruction as StackType;
535
4
                            deferred_while_statements
536
4
                                .push(DeferredWhileStatement::new(begin_location, 0));
537
                        }
538
454
                        "UNTIL" => {
539
2
                            if let Some(x) = deferred_while_statements.pop() {
540
2
                                let pos = tv.len() as StackType;
541
2
                                tv.push(Opcode::LDI(x.begin_location - pos - 1));
542
2
                                tv.push(Opcode::JRZ);
543
                            }
544
                        }
545
452
                        "AGAIN" => {
546
1
                            if let Some(x) = deferred_while_statements.pop() {
547
1
                                let pos = tv.len() as StackType;
548
1
                                tv.push(Opcode::LDI(x.begin_location - pos - 1));
549
1
                                tv.push(Opcode::JMP);
550
                            }
551
                        }
552
451
                        "WHILE" => {
553
1
                            let condition_location = current_instruction as StackType;
554
1
                            tv.push(Opcode::LDI(0));
555
1
                            tv.push(Opcode::JRZ);
556
1
                            let len = deferred_while_statements.len();
557
1
                            deferred_while_statements[len - 1].condition_location =
558
                                condition_location;
559
1
                        }
560
450
                        "REPEAT" => {
561
2
                            if let Some(x) = deferred_while_statements.pop() {
562
                                // jump back up to the begin statements so we can check the condtion again...
563
1
                                let pos = tv.len() as StackType;
564
1
                                tv.push(Opcode::LDI(x.begin_location - pos - 1));
565
1
                                tv.push(Opcode::JMP);
566

            
567
1
                                let repeat_location: StackType = tv.len() as StackType - 1;
568
                                // update while jump location sinc we now know where to jump to if condition is false;
569
1
                                tv[x.condition_location as usize] = Opcode::LDI(
570
1
                                    (repeat_location - x.condition_location) as StackType,
571
                                );
572
                            }
573
                        }
574
449
                        "I" => {
575
13
                            if !deferred_do_statements.is_empty() {
576
13
                                tv.extend_from_slice(&[
577
13
                                    Opcode::RFrom,
578
13
                                    Opcode::RFrom,
579
13
                                    Opcode::TWODUP,
580
13
                                    Opcode::ToR,
581
13
                                    Opcode::ToR,
582
13
                                    Opcode::SWAP,
583
13
                                    Opcode::DROP,
584
13
                                    Opcode::DEBUG("I".to_string()),
585
                                ])
586
13
                            } else {
587
                                todo!();
588
                            }
589
                        }
590
436
                        "?DO" | "DO" => {
591
                            // while i < end  (true (1)
592

            
593
14
                            let do_location = tv.len();
594
14
                            tv.push(Opcode::DEBUG(" in DO".to_string()));
595
14
                            tv.push(Opcode::TWODUP); // copy the end and start values
596
14
                            tv.push(Opcode::ToR); // put the end on the return stack
597
14
                            tv.push(Opcode::ToR); // put the start on the return stack
598

            
599
                            //  let start_location = tv.len();
600
14
                            let comparison_location = tv.len();
601
14
                            tv.push(Opcode::GT); // do the comparison.  will return 0 when false
602
14
                            let incr_location = tv.len();
603
14
                            tv.push(Opcode::LDI(0)); // jump to end of do loop (placeholder)
604
                                                     // tv.push(Opcode::SWAP);
605
14
                            tv.push(Opcode::JRZ); // will jump to instruction after loop if comparison is 0.
606
14
                            deferred_do_statements.push(DeferredDoStatement::new(
607
                                do_location,
608
                                incr_location,
609
                                comparison_location,
610
                            ));
611
                        }
612
422
                        "LOOP" => {
613
9
                            if let Some(x) = deferred_do_statements.pop() {
614
                                //update the instruction so it knows where to jump to when the loop is done.
615
                                //tv[x.do_location]=Opcode::LDI(current_instruction as StackType);
616

            
617
                                //get the index from the return stack
618
                                //   tv.push(Opcode::RFrom);
619
8
                                tv.push(Opcode::DEBUG("IN loop".to_string()));
620
8
                                tv.push(Opcode::RFrom);
621
8
                                tv.push(Opcode::RFrom);
622
                                //add one to it
623
8
                                tv.push(Opcode::LDI(1));
624
8
                                tv.push(Opcode::ADD);
625
                                //tv.push(Opcode::RFrom);
626

            
627
8
                                let pos: StackType = tv.len() as StackType;
628
8
                                tv.push(Opcode::LDI(x.do_location as StackType - pos - 1));
629
8
                                tv.push(Opcode::JR);
630
8
                                let pos: StackType = tv.len() as StackType;
631
8
                                tv[x.incr_location] =
632
8
                                    Opcode::LDI(pos - 1 - x.incr_location as StackType);
633
8
                                tv.push(Opcode::RFrom); // remove end value
634
8
                                tv.push(Opcode::RFrom); // rmeove end value
635
8
                                tv.push(Opcode::RET);
636

            
637
                                // if all goes well, we are done with updating the loop.
638
                            } else {
639
1
                                return Err(ForthError::InvalidSyntax(
640
1
                                    "LOOP without DO".to_owned(),
641
                                ));
642
                            }
643
                        }
644
413
                        "+LOOP" => {
645
7
                            if let Some(x) = deferred_do_statements.pop() {
646
                                //update the instruction so it knows where to jump to when the loop is done.
647
                                //tv[x.do_location]=Opcode::LDI(current_instruction as StackType);
648

            
649
                                //assume last element put on the stack is the increment
650
6
                                let code = tv[tv.len() - 1].clone();
651
6
                                let index: Vec<Opcode> = match code {
652
5
                                    Opcode::LDI(y) => {
653
7
                                        if y < 0 {
654
                                            // when we have a negative increment
655
2
                                            tv[x.comparison_location] = Opcode::LT;
656
                                        }
657
5
                                        vec![Opcode::LDI(y)]
658
                                    }
659
1
                                    Opcode::DEBUG(s) => match s.as_str() {
660
1
                                        "I" => {
661
1
                                            vec![Opcode::DUP, Opcode::DEBUG("I".to_string())]
662
                                        }
663
                                        _ => todo!(),
664
1
                                    },
665
                                    _ => todo!(), // shouldn't ever get here.
666
                                };
667
6
                                tv.push(Opcode::RFrom);
668
6
                                tv.push(Opcode::RFrom);
669
                                //add one to it
670

            
671
6
                                tv.extend_from_slice(&index);
672
6
                                tv.push(Opcode::ADD);
673
                                //tv.push(Opcode::RFrom);
674
                                // put it back on the return stack for the next type through the loop (ie comparison, etc)
675

            
676
6
                                let pos: StackType = tv.len() as StackType;
677
6
                                tv.push(Opcode::LDI(x.do_location as StackType - pos - 1));
678
6
                                tv.push(Opcode::JR);
679
6
                                let pos: StackType = tv.len() as StackType;
680
6
                                tv[x.incr_location] =
681
6
                                    Opcode::LDI(pos - 1 - x.incr_location as StackType);
682
6
                                tv.push(Opcode::RFrom); // remove end value
683
6
                                tv.push(Opcode::RFrom); // rmeove end value
684
6
                                tv.push(Opcode::RET);
685

            
686
                                // if all goes well, we are done with updating the loop.
687
6
                            } else {
688
1
                                return Err(ForthError::InvalidSyntax(
689
1
                                    "LOOP without DO".to_owned(),
690
                                ));
691
                            }
692
6
                        }
693
406
                        "IF" => {
694
16
                            deferred_if_statements
695
16
                                .push(DeferredIfStatement::new(current_instruction));
696
                            //println!("(IF)Deferred If Stack {:?}", deferred_if_statements);
697
                            // put in placeholders just in case we need to jump..
698
16
                            tv.push(Opcode::LDI(0));
699
16
                            tv.push(Opcode::JRZ);
700
                        }
701
390
                        "ELSE" => {
702
10
                            if let Some(x) = deferred_if_statements.last_mut() {
703
9
                                x.else_location = Some(current_instruction);
704
                                //println!("(ELSE) Deferred If Stack {:?}", deferred_if_statements);
705
                                // put in placeholder
706
9
                                tv.push(Opcode::LDI(0));
707
9
                                tv.push(Opcode::JR);
708
                            } else {
709
1
                                return Err(ForthError::InvalidSyntax(
710
1
                                    "ELSE without IF".to_owned(),
711
                                ));
712
                            }
713
                        }
714
380
                        "THEN" => {
715
                            // This only works if there isn't an ELSE statement, it needs to jump differently if there is an ELSE statement
716

            
717
                            //println!("(THEN) Deferred If Stack {:?}", deferred_if_statements);
718
15
                            if let Some(x) = deferred_if_statements.pop() {
719
                                //println!("(if let Some(x)) Deferred If Stack {:?}", x);
720
14
                                let if_jump_location = x.if_location;
721
14
                                let if_jump_offset = match x.else_location {
722
6
                                    None => (current_instruction as usize
723
6
                                        - (x.if_location + 1) as usize)
724
                                        .try_into()
725
                                        .unwrap(),
726
8
                                    Some(el) => (current_instruction as usize - el as usize + 1)
727
                                        .try_into()
728
                                        .unwrap(),
729
                                };
730
14
                                let (else_jump_location, else_jump_offset): (
731
                                    Option<usize>,
732
                                    Option<StackType>,
733
14
                                ) = match x.else_location {
734
16
                                    Some(x) => (
735
8
                                        Some(x),
736
8
                                        Some(
737
8
                                            StackType::try_from(
738
8
                                                current_instruction as usize - (x + 1) as usize,
739
                                            )
740
                                            .unwrap(),
741
                                        ),
742
8
                                    ),
743
6
                                    None => (Some(tv.len()), None),
744
                                };
745
                                //println!("tv: {:?}", tv);
746
                                //println!("if structure: {:?}", x);
747
14
                                tv[if_jump_location] = Opcode::LDI(if_jump_offset);
748
                                //println!("offset:{}", if_jump_offset);
749
                                //println!("else_jump_location:{:?}", else_jump_location);
750
                                //println!("else_jump_offset:{:?}", else_jump_offset);
751

            
752
22
                                if let (Some(location), Some(offset)) =
753
14
                                    (else_jump_location, else_jump_offset)
754
                                {
755
8
                                    tv[location] = Opcode::LDI(offset);
756
                                }
757
                            } else {
758
1
                                return Err(ForthError::InvalidSyntax(
759
1
                                    "THEN without IF".to_owned(),
760
                                ));
761
                            }
762
                        }
763
                        _ => {
764
365
                            if let Some(offset) = self.word_addresses.get(&s.to_uppercase()) {
765
46
                                tv.push(Opcode::LDI(*offset as StackType));
766
46
                                tv.push(Opcode::CALL);
767
319
                            } else if let Some(ol) =
768
319
                                self.intrinsic_words.get::<str>(&s.to_uppercase())
769
                            {
770
307
                                tv.push(ol.clone());
771
36
                            } else if self
772
                                .sm
773
                                .st
774
                                .variable_addresses
775
24
                                .contains_key(&s.to_uppercase())
776
                            {
777
                                // see if this is a variable reference..
778
                                let addr: usize =
779
9
                                    match self.sm.st.variable_addresses.get(&s.to_uppercase()) {
780
9
                                        Some(x) => *x as usize,
781
                                        _ => todo!(),
782
9
                                    };
783
9
                                tv.push(Opcode::LDI(addr as StackType));
784
3
                            } else if self.sm.st.constants.contains_key(&s.to_uppercase()) {
785
2
                                let value = match self.sm.st.constants.get(&s.to_uppercase()) {
786
2
                                    Some(x) => x,
787
                                    _ => todo!(),
788
2
                                };
789
2
                                tv.push(Opcode::LDI(*value));
790
                            } else {
791
1
                                return Err(ForthError::UnknownToken(s.to_string()));
792
319
                            }
793
365
                        }
794
                    }
795
458
                }
796
                // Token::SDoubleTick(s) => {
797
                //     tv.push(Opcode::SDoubleTick(s.to_string()));
798
                // }
799
                Token::Period => {
800
                    tv.push(Opcode::PRINT);
801
                    //print out TOS and remove element from stack;
802
                }
803
2
                Token::Comment(_s) => {} //skip for now. can ignore comments.
804
                Token::Colon(_) => {
805
                    panic!("Colon should never reach this function");
806
                }
807
                Token::SemiColon => {
808
                    panic!("SemiColon should never reach this function");
809
                }
810
                Token::End => {
811
                    panic!("Token::End not coded yet");
812
                }
813
                Token::Error(_) => {
814
                    panic!("Token::Error not coded yet");
815
                }
816
            }
817
        }
818

            
819
        //println!("Compiled Codes {:?}", tv);
820
        //println!("Total size of Codes {:?}", tv.len());
821
214
        if !deferred_if_statements.is_empty() {
822
2
            Err(ForthError::InvalidSyntax("If without then".to_string()))
823
        } else {
824
210
            Ok(tv)
825
        }
826
218
    }
827

            
828
158
    fn execute_token_vector(
829
        &mut self,
830
        token_vector: &[Token],
831
        gas_limit: GasLimit,
832
    ) -> Result<String, ForthError> {
833
158
        let mut ol = self.compile_token_vector_compile_and_remove_word_definitions(token_vector)?;
834
        //println!("Compiled Opcodes: {:?}", ol);
835
148
        self.sm.st.opcodes.resize(self.last_function, Opcode::NOP);
836
148
        self.sm.st.opcodes.append(&mut ol);
837
148
        match self.sm.execute(self.last_function, gas_limit) {
838
145
            Ok(x) => Ok(x),
839
3
            Err(x) => Err(x.into()),
840
        }
841
        //println!("Total opcodes defined: {}", self.sm.st.opcodes.len());
842
        //println!("Total opcodes executed: {}", self.sm.st.gas_used());
843
158
    }
844

            
845
163
    pub fn execute_string(&mut self, s: &str, gas_limit: GasLimit) -> Result<String, ForthError> {
846
163
        let tv = self.tokenize_string(s)?;
847
158
        self.execute_token_vector(&tv, gas_limit)
848
163
    }
849
}
850

            
851
#[cfg(test)]
852
mod tests {
853
    use super::*;
854

            
855
    #[test]
856
2
    fn test_char() {
857
1
        let mut fc = ForthCompiler::new();
858

            
859
        // push 4 to return stack and then back to the parameter stack.
860
1
        assert_eq!(
861
1
            fc.execute_string(
862
                "CHAR F EMIT CHAR O EMIT CHAR R EMIT CHAR T EMIT CHAR H EMIT",
863
1
                GasLimit::Limited(100)
864
            )
865
            .unwrap(),
866
1
            "FORTH".to_string()
867
        );
868

            
869
1
        match fc.execute_string("CHAR EMIT", GasLimit::Limited(100)) {
870
            Err(ForthError::InvalidSyntax(_)) => (),
871
            r => panic!("Incorrect error type returned {:?}", r),
872
        }
873
2
    }
874

            
875
    #[test]
876
2
    fn test_syntax_errors() {
877
1
        let mut fc = ForthCompiler::new();
878

            
879
        // push 4 to return stack and then back to the parameter stack.
880
1
        match fc.execute_string("2 Loop", GasLimit::Limited(100)) {
881
            Err(ForthError::InvalidSyntax(_)) => (),
882
            r => panic!("Incorrect error type returned {:?}", r),
883
1
        }
884

            
885
1
        match fc.execute_string("2 +Loop", GasLimit::Limited(100)) {
886
            Err(ForthError::InvalidSyntax(_)) => (),
887
            r => panic!("Incorrect error type returned {:?}", r),
888
        }
889
2
    }
890

            
891
    #[test]
892
2
    fn test_plus_loop() {
893
1
        let mut fc = ForthCompiler::new();
894

            
895
        // push 4 to return stack and then back to the parameter stack.
896
1
        fc.execute_string(": PJ  50 0 do i . 5 +loop ;", GasLimit::Limited(100))
897
1
            .unwrap();
898
1
        assert_eq!(
899
1
            fc.execute_string("PJ", GasLimit::Limited(500)).unwrap(),
900
1
            "0 5 10 15 20 25 30 35 40 45 ".to_string()
901
        );
902

            
903
1
        fc.execute_string(": PJ  100 0 do i . 10 +loop ;", GasLimit::Limited(100))
904
1
            .unwrap();
905
1
        assert_eq!(
906
1
            fc.execute_string("PJ", GasLimit::Limited(500)).unwrap(),
907
1
            "0 10 20 30 40 50 60 70 80 90 ".to_string()
908
        );
909
2
    }
910

            
911
    #[test]
912
2
    fn test_r_functions() {
913
1
        let mut fc = ForthCompiler::new();
914

            
915
        // push 4 to return stack and then back to the parameter stack.
916
1
        fc.execute_string("2 4 >R R>", GasLimit::Limited(100))
917
1
            .unwrap();
918
1
        assert_eq!(fc.sm.st.data_stack, vec![2, 4]);
919
2
    }
920

            
921
    #[test]
922
2
    fn random() {
923
1
        let mut fc = ForthCompiler::new();
924

            
925
1
        fc.execute_string("rand rand", GasLimit::Limited(100))
926
1
            .unwrap();
927
1
        assert_eq!(fc.sm.st.data_stack, vec![-7961, 4615]);
928

            
929
1
        fc.execute_string("100 setseed", GasLimit::Limited(100))
930
1
            .unwrap();
931
1
        assert_eq!(fc.sm.st.data_stack, vec![-7961, 4615]);
932

            
933
1
        fc.execute_string("rand rand", GasLimit::Limited(100))
934
1
            .unwrap();
935
1
        assert_eq!(fc.sm.st.data_stack, vec![-7961, 4615, -29662, -32274]);
936
2
    }
937

            
938
    #[test]
939
2
    fn test_zeroless() {
940
1
        let mut fc = ForthCompiler::new();
941

            
942
1
        fc.execute_string("1 0<", GasLimit::Limited(100)).unwrap();
943
1
        assert_eq!(fc.sm.st.data_stack, vec![0]);
944

            
945
1
        fc.execute_string("-1 0<", GasLimit::Limited(100)).unwrap();
946
1
        assert_eq!(fc.sm.st.data_stack, vec![0, 1]);
947
2
    }
948

            
949
    #[test]
950
2
    fn test_zeroequal() {
951
1
        let mut fc = ForthCompiler::new();
952

            
953
1
        fc.execute_string("0 0=", GasLimit::Limited(100)).unwrap();
954
1
        assert_eq!(fc.sm.st.data_stack, vec![1]);
955

            
956
1
        fc.execute_string("0=", GasLimit::Limited(100)).unwrap();
957
1
        assert_eq!(fc.sm.st.data_stack, vec![0]);
958
2
    }
959

            
960
    #[test]
961
2
    fn test_zerogreater() {
962
1
        let mut fc = ForthCompiler::new();
963

            
964
1
        fc.execute_string("1 0>", GasLimit::Limited(100)).unwrap();
965
1
        assert_eq!(fc.sm.st.data_stack, vec![1]);
966

            
967
1
        fc.execute_string("0 0>", GasLimit::Limited(100)).unwrap();
968
1
        assert_eq!(fc.sm.st.data_stack, vec![1, 0]);
969
2
    }
970

            
971
    #[test]
972
2
    fn test_comments() {
973
1
        let mut fc = ForthCompiler::new();
974

            
975
1
        fc.execute_string("( this is a comment)", GasLimit::Limited(100))
976
1
            .unwrap();
977
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
978

            
979
1
        fc.execute_string(r#"2 2 + \ comment, comment"#, GasLimit::Limited(100))
980
1
            .unwrap();
981
1
        assert_eq!(fc.sm.st.data_stack, vec![4]);
982
2
    }
983

            
984
    #[test]
985
2
    fn test_twoswap() {
986
1
        let mut fc = ForthCompiler::new();
987
1
        fc.execute_string("3 2 1 0 2swap", GasLimit::Limited(100))
988
1
            .unwrap();
989
1
        assert_eq!(fc.sm.st.data_stack, vec![0, 1, 2, 3]);
990
2
    }
991

            
992
    #[test]
993
2
    fn test_tworot() {
994
1
        let mut fc = ForthCompiler::new();
995
1
        fc.execute_string("1 2 3 4 5 6 2rot", GasLimit::Limited(100))
996
1
            .unwrap();
997
1
        assert_eq!(fc.sm.st.data_stack, vec![3, 4, 5, 6, 1, 2]);
998
1
        fc.execute_string("2rot", GasLimit::Limited(100)).unwrap();
999
1
        assert_eq!(fc.sm.st.data_stack, vec![5, 6, 1, 2, 3, 4]);
2
    }
    #[test]
2
    fn test_twoover() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("1 2 3 4 5 6 2over", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![1, 2, 3, 4, 5, 6, 3, 4]);
1
        fc.execute_string("2over", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![1, 2, 3, 4, 5, 6, 3, 4, 5, 6]);
2
    }
    #[test]
2
    fn test_questiondup() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("1 0 ?DUP", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![1, 0]);
1
        fc.sm.st.data_stack.clear();
1
        fc.execute_string("1 1 ?DUP", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![1, 1, 1]);
2
    }
    #[test]
2
    fn test_depth() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("Depth", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![0]);
1
        fc.execute_string("Depth", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![0, 1]);
2
    }
    #[test]
2
    fn test_2drop() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("2 3", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![2, 3]);
1
        fc.execute_string("2drop", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
2
    }
    #[test]
2
    fn test_1plus() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("2 1+", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![3]);
2
    }
    #[test]
2
    fn test_1minus() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("2 1-", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![1]);
2
    }
    #[test]
2
    fn test_2plus() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("2 2+", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![4]);
2
    }
    #[test]
2
    fn test_2minus() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("2 2-", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![0]);
2
    }
    #[test]
2
    fn test_spaces() {
1
        let mut fc = ForthCompiler::new();
1
        assert_eq!(
1
            fc.execute_string("SPACE", GasLimit::Limited(100)).unwrap(),
            " "
        );
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
1
        assert_eq!(
1
            fc.execute_string("3 SPACES", GasLimit::Limited(100))
                .unwrap(),
            "   "
        );
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
2
    }
    #[test]
2
    fn test_text() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(r#" ." this is a comment""#, GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
2
    }
    #[test]
2
    fn test_constants() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("314 constant PI", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
1
        fc.execute_string("PI", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![314]);
2
    }
    #[test]
2
    fn test_variables() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("variable abc", GasLimit::Limited(100))
1
            .unwrap();
1
        fc.execute_string("123 abc !", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(fc.sm.st.data_stack.len(), 0);
1
        fc.execute_string("abc @", GasLimit::Limited(100)).unwrap();
1
        assert_eq!(fc.sm.st.data_stack, vec![123]);
2
    }
    #[test]
2
    fn test_execute_intrinsics_1() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("123 321 + 2 *", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888]);
1
        fc.execute_string("123 321 + 2 *", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888, 888]);
2
    }
    #[test]
2
    fn test_compile_1() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(
            ": RickTest 123 321 + 2 * ; RickTest",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888]);
1
        fc.execute_string("123 321 + 2 * RickTest", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888, 888, 888]);
2
    }
    #[test]
2
    fn test_compile_2() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(
            ": RickTest 123 321 + 2 * ; RickTest : RickTestB 123 321 + 2 * ;",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888]);
1
        fc.execute_string("123 321 + 2 * RickTest", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888, 888, 888]);
2
    }
    #[test]
2
    fn test_compile_3() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(
            "2 2 - DROP : RickTest 123 321 + 2 * ; RickTest : RickTestB 123 321 + 2 * ; 3 3 - DROP",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888]);
1
        fc.execute_string("123 321 + 2 * RickTest", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![888, 888, 888]);
2
    }
    #[test]
2
    fn test_compile_4() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(
            "2 2 - DROP : RickTest 123 321 + 2 * ; : RickTestB 123 321 + 2 * ; 3 3 -",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![0]);
1
        fc.execute_string("123 321 + 2 * RickTest", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![0, 888, 888]);
2
    }
    #[test]
2
    fn test_compile_fail_1() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string(
            "2 2 - DROP : RickTest 123 321 + 2 * ; : : RickTestB 123 321 + 2 * ; 3 3 -",
1
            GasLimit::Limited(100),
        ) {
1
            Err(ForthError::UnknownToken(ref x)) if x == "RickTestB" => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_compile_fail_2() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string(
            "2 2 - DROP : RickTest 123 321 + 2 * ; ; : RickTestB 123 321 + 2 * ; 3 3 -",
1
            GasLimit::Limited(100),
        ) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_compile_fail_3() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string(
            "2 2 - DROP : RickTest 123 321 + 2 * ; : RickTestB 123 321 + 2 * ; : ERROR 3 3 -",
1
            GasLimit::Limited(100),
        ) {
            Err(ForthError::MissingSemicolonAfterColon) => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_compile_syntax() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string(": ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
1
        }
        // todo
1
        match fc.execute_string(": myadd 2 3 : ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (), //returning wrong error message..
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_variable_syntax() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string("variable ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_constant_syntax() {
1
        let mut fc = ForthCompiler::new();
1
        match fc.execute_string("constant ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
1
        }
1
        fc.execute_string("69 constant abc", GasLimit::Limited(100))
1
            .unwrap();
1
        match fc.execute_string("constant abc ", GasLimit::Limited(100)) {
            Err(ForthError::ConstantAlreadyExists(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_if_syntax() {
1
        let mut fc = ForthCompiler::new();
        // todo!
1
        match fc.execute_string(": myfunc  1 2 IF ; ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (), // should return an error..
            r => panic!("Incorrect error type returned {:?}", r),
1
        }
        //todo
1
        match fc.execute_string(": myfunc  1 2 if 1 + else ; ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (), // should return an error
            r => panic!("Incorrect error type returned {:?}", r),
1
        }
        //todo
1
        match fc.execute_string(": myfunc  1 2 else then ; ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
1
        }
        //todo
1
        match fc.execute_string(": myfunc  1 2 then ; ", GasLimit::Limited(100)) {
            Err(ForthError::InvalidSyntax(_)) => (),
            r => panic!("Incorrect error type returned {:?}", r),
        }
2
    }
    #[test]
2
    fn test_print() {
1
        let mut fc = ForthCompiler::new();
1
        assert_eq!(
1
            fc.execute_string("42 .", GasLimit::Limited(100)).unwrap(),
            "42 "
        );
1
        assert_eq!(&fc.sm.st.data_stack, &vec![]);
2
    }
    #[test]
2
    fn test_if1() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("0 IF 1 2 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![]);
2
    }
    #[test]
2
    fn test_if2() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("1 IF 1 2 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![3]);
2
    }
    #[test]
2
    fn test_if_else_1() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("0 IF 1 2 + ELSE 3 4 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![7]);
2
    }
    #[test]
2
    fn test_if_else_2() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("1 IF 1 2 + ELSE 3 4 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![3]);
2
    }
    #[test]
2
    fn test_if_else_3() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("-1 IF 1 2 + ELSE 3 4 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![3]);
2
    }
    #[test]
2
    fn test_if_else_4() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("1 IF 1 2 + 1 + ELSE 3 4 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![4]);
2
    }
    #[test]
2
    fn test_if_else_5() {
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string("0 IF 1 2 + 1 + ELSE 3 4 + 1 + THEN", GasLimit::Limited(100))
1
            .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![8]);
2
    }
    #[test]
2
    fn test_if_else_6() {
        // emulates MAX
1
        let mut fc = ForthCompiler::new();
1
        fc.execute_string(
            "10 20 DUP DUP > IF DROP + ELSE SWAP DROP THEN",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
1
        assert_eq!(&fc.sm.st.data_stack, &vec![20]);
2
    }
    #[test]
2
    fn test_trap_1() {
1
        let mut fc = ForthCompiler::new();
        // Simulate a IO OUT command, at TRAP(100)
2
        fc.sm
            .trap_handlers
2
            .push(Box::from(TrapHandler::new(100, |_trap_id, st| {
1
                let io_port = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                let io_value = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                println!(
                    "Simulated IO OUT command to Port: {} and Value: {}",
                    io_port, io_value
                );
1
                Ok(TrapHandled::Handled)
1
            })));
1
        fc.execute_string(
            ": IO_OUT 100 TRAP ; 1234 1000 IO_OUT",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
        // Nothing left over
1
        assert_eq!(&fc.sm.st.data_stack, &vec![]);
2
    }
    #[test]
2
    fn test_trap_2() {
1
        let mut fc = ForthCompiler::new();
        // Simulate a IO IN command, at TRAP(101)
2
        fc.sm
            .trap_handlers
2
            .push(Box::from(TrapHandler::new(101, |_trap_id, st| {
1
                let io_port = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                let io_value = 6543;
1
                println!(
                    "Simulated IO IN command from Port: {} and Value: {}",
                    io_port, io_value
                );
1
                st.data_stack.push(io_value);
1
                Ok(TrapHandled::Handled)
1
            })));
1
        fc.execute_string(": IO_IN 101 TRAP ; 1000 IO_IN", GasLimit::Limited(100))
1
            .unwrap();
        // Value from IO port on stack
1
        assert_eq!(&fc.sm.st.data_stack, &vec![6543]);
2
    }
    #[test]
2
    fn test_trap_3() {
1
        let mut fc = ForthCompiler::new();
        // Simulate a IO OUT command, at TRAP(100), but define the port number inside a Forth Word as well
2
        fc.sm
            .trap_handlers
2
            .push(Box::from(TrapHandler::new(100, |_trap_id, st| {
1
                let io_port = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                let io_value = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                println!(
                    "Simulated IO OUT command to Port: {} and Value: {}",
                    io_port, io_value
                );
1
                Ok(TrapHandled::Handled)
1
            })));
1
        fc.execute_string(
            ": IO_OUT 100 TRAP ; : OUT_DISPLAY 1000 IO_OUT ; 1234 OUT_DISPLAY",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
        // Nothing left over
1
        assert_eq!(&fc.sm.st.data_stack, &vec![]);
2
    }
    #[test]
2
    fn test_trap_4() {
1
        let mut fc = ForthCompiler::new();
        // Simulate a IO IN command, at TRAP(101), but define the port number inside a Forth word as well
2
        fc.sm
            .trap_handlers
2
            .push(Box::from(TrapHandler::new(101, |_trap_id, st| {
1
                let io_port = st
                    .data_stack
                    .pop()
1
                    .ok_or(StackMachineError::NumberStackUnderflow)?;
1
                let io_value = 6543;
1
                println!(
                    "Simulated IO IN command from Port: {} and Value: {}",
                    io_port, io_value
                );
1
                st.data_stack.push(io_value);
1
                Ok(TrapHandled::Handled)
1
            })));
1
        fc.execute_string(
            ": IO_IN 101 TRAP ; : IN_KEYBOARD 1000 IO_IN ; IN_KEYBOARD",
1
            GasLimit::Limited(100),
        )
1
        .unwrap();
        // Value from IO port on stack
1
        assert_eq!(&fc.sm.st.data_stack, &vec![6543]);
2
    }
}