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
// Copyright (C) 2022 Laurent Wandrebeck
//
// This file is part of ripeg.
//
// ripeg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ripeg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ripeg.  If not, see <http://www.gnu.org/licenses/>.

//! isa provides types for all instructions of ripeg VM. aka
//! Instruction Set Architecture.

use crate::charset::NormalSet;

/// Instruction Pointer
/// Can get either next instruction address to be executed or None (failure state)
pub enum IP {
    /// Fail state
    None,
    /// actual position in the current program
    Index(usize),
}

/// List of Instructions
pub enum Instr {
    /// Advances ip and consume n u8 from subject if possible, fails otherwise.
    /// Fails only by reaching the end of the subject.
    Any(usize),
    /// pops backtrack entry from the stack, updates sp and jumps to usize
    BackCommit(usize),
    /// Pushes the next [`IP`] to the stack as a return address and jumps to usize.
    /// Used to implement non-terminals.
    Call(usize),
    /// Increment [`IP`] and Consume u8 if it matches, ip becomes None otherwise.
    Char(u8),
    /// Pushes a backtrack entry storing u8 and sp so that parser can backtrack
    /// to this position later and parse a different pattern.
    Choice(usize),
    /// Pops the top entry off the stack and jumps to usize. Allows the machine to
    /// commit to a state and discard a backtrack entry.
    Commit(usize),
    /// Ends matching and accepts the subject.
    End,
    /// Ends matching and rejects the subject.
    EndFail,
    /// Sets ip to None.
    Fail,
    /// pops top entry from the stack and sets IP to None
    FailTwice,
    /// Sets ip Index to usize.
    Jump(usize),
    /// used to mark a location in the instruction code with an unique ID.
    /// Maybe we could get rid of that ?
    /// Likely to be deleted as Label will probably become a HashMap<Label, Program[index]
    Label(usize),
    /// No operation
    /// Likely to be deleted ?
    Nop,
    /// Commit and Choice in 1 instruction
    PartialCommit(usize),
    /// Pops a return address from the stack and jumps to it.
    Return,
    /// advances ip and consumes u8 from subject if contained in character set [`NormalSet`].
    /// Goes to [`Instr::Fail`] state otherwise.
    Set(NormalSet),
    /// [`Instr::Span`] equals to [`NormalSet`]*
    Span(NormalSet),
    /// checks if there is at least 1st arg characters remaining -> pushes backtrack
    /// entry and advances sp by 1st arg. if not jumps to 2nd arg.
    TestAny(usize, usize),
    /// checks if 1st arg matches at current sp -> pushes backtrack entry on stack
    /// and advances sp. if not, jumps to 2nd arg.
    TestChar(u8, usize),
    /// checks if 1st arg matches at sp -> advances sp. if not jumps to 2nd arg.
    TestCharNoChoice(u8, usize),
    /// checks if 1st arg (Set) matches at current sp -> pushes backtrack entry on stack
    /// and advances sp. if not, jumps to 2nd arg.
    TestSet(NormalSet, usize),
    /// Checks if NormalSet matches at sp -> advances sp. If not jumps to 2nd arg.
    TestSetNoChoice(NormalSet, usize),
}

/// A Program is a Vector of Instructions.
/// struct used here because type does not allow to use impl.
pub struct Program(Vec<Instr>);

/// Program is instanciated by default without arguments, so define
/// a Default implementation.
impl Default for Program {
    /// Clippy says so.
    fn default() -> Self {
        Program::new()
    }
}

/// Methods for a [`Program`]
impl Program {
    /// Instanciate a new [`Program`]
    ///
    /// # Examples
    /// ```
    /// use crate::ripeg::isa::Program;
    /// let p = Program::new();
    /// assert_eq!(p.size(), 0);
    /// ```
    pub fn new() -> Self {
        Self(Vec::new())
    }

    /// Size of a Program (# of Instr minus Label and Nop)
    /// Likely to change as Label will probably become a HashMap<Label, Program[index]
    ///
    /// # Examples
    /// ```
    /// use crate::ripeg::isa::Program;
    /// let p = Program::new();
    /// assert_eq!(p.size(), 0);
    /// ```
    pub fn size(&self) -> usize {
        let mut s = 0;
        for i in &self.0 {
            match i {
                Instr::Label(_) | Instr::Nop => continue,
                _ => s += 1,
            }
        }
        s
    }
}