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
use crate::PreCompileProgram;
use failure::Error;
use lincoln_common::traits::{Access, AccessMut};
use lincoln_compiled::Permutation;
use std::fmt::{Debug, Display, Formatter};

/// 5 types of entries correspond to 5 different
/// instructions the use type.
///
/// Jmp: permutate the context to have specific order,
///      then jump to another entry (not a group!)
///
/// Call: keep a specific amount of values from context, then
///       build a closure with the rest, and add the closure into
///       the context. Finally, jump to another entry.
///
/// Ret: The first variable must be a closure to extract the context.
///      The specified variant of the closure group entry
///      will be invoked.
///
/// Group: Define a group of entries. Being used to create closures.
///
/// Extern: Denotes a function that to be executed in the outside world.
///
#[derive(Serialize, Deserialize)]
pub enum Entry {
    Jmp {
        cont: EntryRef,
        per: Permutation,
    },
    Call {
        callee: EntryRef,
        callcnt: u8,
        callcont: EntryRef,
    },
    Ret {
        variant: u8,
    },
    Group {
        elements: Vec<EntryRef>,
    },
    Extern {
        name: String,
    },
}
impl Debug for Entry {
    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "{}", self)
    }
}
impl Entry {
    fn is_group(&self) -> bool {
        match self {
            Entry::Group { .. } => true,
            _ => false,
        }
    }
}
impl Display for Entry {
    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        match self {
            Entry::Jmp { cont, per } => write!(fmt, "jmp {} #!{}", cont, per),
            Entry::Call {
                callee,
                callcnt,
                callcont,
            } => write!(fmt, "call {} {} {}", callee, callcnt, callcont),
            Entry::Ret { variant } => write!(fmt, "ret {}", variant),
            Entry::Group { elements } => {
                write!(fmt, "group")?;
                let it = elements.iter().enumerate();
                for (idx, element) in it {
                    write!(fmt, " {}:{}", idx, *element)?;
                }
                Ok(())
            }
            Entry::Extern { name } => write!(fmt, "extern {}", name),
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct EntryRef {
    index: usize,
}
impl EntryRef {
    pub fn is_group_in(&self, pm: &PreCompileProgram) -> bool {
        if let Ok(v) = self.access(pm) {
            v.is_group()
        } else {
            false
        }
    }
    pub fn new(index: usize) -> Self {
        EntryRef { index }
    }
}
impl Debug for EntryRef {
    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "{}", self)
    }
}
impl Display for EntryRef {
    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
        write!(fmt, "#{}", self.index)
    }
}
impl<'a> Access<'a, PreCompileProgram> for EntryRef {
    type Target = Result<&'a Entry, Error>;
    fn access<'b>(&self, src: &'b PreCompileProgram) -> Self::Target
    where
        'b: 'a,
    {
        src.entry(self.index)
    }
}
impl<'a> AccessMut<'a, PreCompileProgram> for EntryRef {
    type Target = Result<&'a mut Entry, Error>;
    fn access_mut<'b>(&self, src: &'b mut PreCompileProgram) -> Self::Target
    where
        'b: 'a,
    {
        src.entry_mut(self.index)
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_debug() {
        use crate::entry::{Entry, EntryRef};
        let ent = Entry::Jmp {
            cont: EntryRef::new(0),
            per: "ba".parse().unwrap(),
        };
        assert_eq!(format!("{:?}", ent), "jmp #0 #!ba");
        let ent = Entry::Call {
            callee: EntryRef::new(1),
            callcnt: 3,
            callcont: EntryRef::new(2),
        };
        assert_eq!(format!("{:?}", ent), "call #1 3 #2");
        let ent = Entry::Ret { variant: 2 };
        assert_eq!(format!("{:?}", ent), "ret 2");
        let ent = Entry::Extern { name: "ext".into() };
        assert_eq!(format!("{:?}", ent), "extern ext");
        let ent = Entry::Group {
            elements: vec![EntryRef::new(3), EntryRef::new(4)],
        };
        assert_eq!(format!("{:?}", ent), "group 0:#3 1:#4");
    }
    #[test]
    fn test_display() {
        use crate::entry::{Entry, EntryRef};
        let ent = Entry::Jmp {
            cont: EntryRef::new(0),
            per: "ba".parse().unwrap(),
        };
        assert_eq!(format!("{}", ent), "jmp #0 #!ba");
        let ent = Entry::Call {
            callee: EntryRef::new(1),
            callcnt: 3,
            callcont: EntryRef::new(2),
        };
        assert_eq!(format!("{}", ent), "call #1 3 #2");
        let ent = Entry::Ret { variant: 2 };
        assert_eq!(format!("{}", ent), "ret 2");
        let ent = Entry::Extern { name: "ext".into() };
        assert_eq!(format!("{}", ent), "extern ext");
        let ent = Entry::Group {
            elements: vec![EntryRef::new(3), EntryRef::new(4)],
        };
        assert_eq!(format!("{}", ent), "group 0:#3 1:#4");
    }
}