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
use parser::ast;
use std::{
    collections::{hash_map::Entry, HashMap},
    fmt,
    rc::Rc,
};
use strtab::Symbol;

#[derive(Debug)]
pub struct ClassDoesNotExist;
#[derive(Debug)]
pub struct ClassAlreadyDeclared;

#[derive(Debug, Default)]
pub struct TypeSystem<'src, 'ast> {
    pub defined_classes: HashMap<Symbol<'src>, Rc<ClassDef<'src, 'ast>>>,
}

impl<'src, 'ast> TypeSystem<'src, 'ast> {
    pub fn is_type_defined(&self, name: Symbol<'src>) -> bool {
        self.defined_classes.contains_key(&name)
    }

    pub fn add_class_def<'a>(
        &'a mut self,
        class_def: ClassDef<'src, 'ast>,
    ) -> Result<ClassDefId<'src>, ClassAlreadyDeclared> {
        match self.defined_classes.entry(class_def.name) {
            Entry::Occupied(_) => Err(ClassAlreadyDeclared),
            Entry::Vacant(e) => {
                let id = ClassDefId { id: class_def.name };
                e.insert(Rc::new(class_def));
                Ok(id)
            }
        }
    }

    pub fn class_mut(&mut self, id: ClassDefId<'src>) -> &mut ClassDef<'src, 'ast> {
        self.defined_classes
            .get_mut(&id.id)
            .and_then(Rc::get_mut)
            .expect("Ids always point to existing classes")
    }

    pub fn class(&self, id: ClassDefId<'src>) -> Rc<ClassDef<'src, 'ast>> {
        self.defined_classes
            .get(&id.id)
            .map(Rc::clone)
            .expect("Ids always point to existing classes")
    }

    pub fn lookup_class_mut(&mut self, name: Symbol<'src>) -> Option<&mut ClassDef<'src, 'ast>> {
        self.defined_classes.get_mut(&name).and_then(Rc::get_mut)
    }

    pub fn lookup_class(
        &self,
        name: Symbol<'src>,
    ) -> Option<(Rc<ClassDef<'src, 'ast>>, ClassDefId<'src>)> {
        match self.defined_classes.get(&name) {
            Some(class) => {
                let id = ClassDefId { id: name };
                Some((Rc::clone(class), id))
            }
            None => None,
        }
    }

    pub fn defined_classes(&self) -> &HashMap<Symbol<'_>, Rc<ClassDef<'_, '_>>> {
        &self.defined_classes
    }
}

/// A `ClassDefId` refers to a class definition.
///
/// Having an instance of this struct ensures that
/// the type system that issued this instance can
/// provide the definition of that class.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ClassDefId<'src> {
    id: Symbol<'src>,
}

impl<'src, 'ts> From<ClassDefId<'src>> for CheckedType<'src> {
    fn from(id: ClassDefId<'src>) -> CheckedType<'src> {
        CheckedType::TypeRef(id)
    }
}

impl<'src> fmt::Display for ClassDefId<'src> {
    fn fmt(&self, f: &'_ mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.id)
    }
}

impl<'src> ClassDefId<'src> {
    pub fn as_str(&self) -> &str {
        self.id.as_str()
    }

    pub fn id(&self) -> Symbol<'src> {
        self.id
    }
}

#[derive(Debug)]
pub struct ClassDef<'src, 'ast> {
    // tracks how many redefinitions there are
    redefinitions: usize,
    pub name: Symbol<'src>,
    fields: HashMap<Symbol<'src>, Rc<ClassFieldDef<'src>>>,
    methods: HashMap<Symbol<'src>, Rc<ClassMethodDef<'src, 'ast>>>,
    pub comparable: bool,
}

impl<'src, 'ast> ClassDef<'src, 'ast> {
    pub fn new(name: Symbol<'src>) -> ClassDef<'src, 'ast> {
        ClassDef {
            redefinitions: 0,
            name,
            fields: HashMap::new(),
            methods: HashMap::new(),
            comparable: true,
        }
    }

    pub fn get_new_redefinition_number(&mut self) -> usize {
        self.redefinitions += 1;
        self.redefinitions
    }

    pub fn add_field(&mut self, field: ClassFieldDef<'src>) -> Result<(), ()> {
        match self.fields.entry(field.name) {
            Entry::Occupied(_) => return Err(()),
            Entry::Vacant(e) => e.insert(Rc::new(field)),
        };
        Ok(())
    }

    pub fn field_names(&self) -> Vec<&Symbol<'src>> {
        self.fields.keys().collect()
    }

    pub fn field(&self, name: Symbol<'src>) -> Option<Rc<ClassFieldDef<'src>>> {
        self.fields.get(&name).map(Rc::clone)
    }

    pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = Rc<ClassFieldDef<'src>>> + 'a {
        self.fields.iter().map(|(_, c)| Rc::clone(c))
    }

    pub fn iter_methods<'a>(&'a self) -> impl Iterator<Item = Rc<ClassMethodDef<'src, 'ast>>> + 'a {
        self.methods.iter().map(|(_, c)| Rc::clone(c))
    }

    pub fn add_method(&mut self, method: ClassMethodDef<'src, 'ast>) -> Result<(), ()> {
        match self.methods.entry(method.name) {
            Entry::Occupied(_) => return Err(()),
            Entry::Vacant(e) => e.insert(Rc::new(method)),
        };
        Ok(())
    }

    pub fn method(&self, name: Symbol<'src>) -> Option<Rc<ClassMethodDef<'src, 'ast>>> {
        self.methods.get(&name).map(Rc::clone)
    }
}

#[derive(Debug, Clone, Copy)]
pub enum BuiltinMethodBody {
    SystemOutPrintln,
    SystemOutWrite,
    SystemOutFlush,
    SystemInRead,
}

pub type Body<'src, 'ast> = asciifile::spanned::Spanned<'src, ast::Block<'src>>;

#[derive(Debug, Clone, Copy)]
pub enum ClassMethodBody<'src, 'ast> {
    Builtin(BuiltinMethodBody),
    AST(&'ast Body<'src, 'ast>),
}

#[derive(Debug)]
pub struct ClassMethodDef<'src, 'ast> {
    /// Name of the method
    pub name: Symbol<'src>,
    pub body: ClassMethodBody<'src, 'ast>,
    /// params does not include `this` for non-static / non-main methods
    pub params: Vec<Rc<MethodParamDef<'src>>>,
    pub return_ty: CheckedType<'src>,
    pub is_static: bool,
    pub is_main: bool,
}

impl<'src, 'ast> ClassMethodDef<'src, 'ast> {
    pub fn new(
        name: Symbol<'src>,
        body: ClassMethodBody<'src, 'ast>,
        params: Vec<MethodParamDef<'src>>,
        return_ty: CheckedType<'src>,
        is_static: bool,
    ) -> ClassMethodDef<'src, 'ast> {
        ClassMethodDef {
            is_static,
            name,
            body,
            return_ty,
            params: params.into_iter().map(Rc::new).collect(),
            is_main: is_static,
        }
    }
}

#[derive(Debug)]
pub struct MethodParamDef<'src> {
    pub name: Symbol<'src>,
    pub ty: CheckedType<'src>,
}

impl<'src> MethodParamDef<'src> {
    pub fn new(name: Symbol<'src>, ty: CheckedType<'src>) -> MethodParamDef<'src> {
        MethodParamDef { name, ty }
    }
}

#[derive(Debug)]
pub struct ClassFieldDef<'src> {
    /// Name of the field
    pub name: Symbol<'src>,
    pub ty: CheckedType<'src>,
    pub can_write: bool,
}

// FIXME Clone or not? => Store types in hashmap
#[derive(Debug, Clone, PartialEq)]
pub enum CheckedType<'src> {
    Int,
    Boolean,
    Void,
    Null,
    TypeRef(ClassDefId<'src>),
    UnknownType(Symbol<'src>),
    Array(Box<CheckedType<'src>>),
}

impl<'src> CheckedType<'src> {
    pub fn create_array_type(item_type: CheckedType<'src>, dimension: u64) -> CheckedType<'src> {
        if dimension == 0 {
            item_type
        } else {
            CheckedType::Array(Box::new(CheckedType::create_array_type(
                item_type,
                dimension - 1,
            )))
        }
    }

    pub fn inner_type(&self) -> Option<&CheckedType<'src>> {
        match self {
            CheckedType::Array(ty) => Some(&*ty),
            _ => None,
        }
    }

    pub fn is_assignable_from(
        &self,
        other: &CheckedType<'src>,
        ts: &'_ TypeSystem<'src, '_>,
    ) -> bool {
        use self::CheckedType::*;

        #[allow(clippy::match_same_arms)]
        match self {
            // dont generate errors for unknown types as they are invalid anyways
            UnknownType(_) => true,
            Int | Boolean => self == other,
            // nothing is assignable to null or void, not even expressions of type void or null.
            // This does not really matter though as void is not an inhibited type.
            // However, to improve error messages, we allow assigning void to void.
            Null | Void => self == other,
            Array(item_ty) => match other {
                Null => true,
                Array(other_item_ty) => item_ty.is_assignable_from(other_item_ty, ts),
                _ => false,
            },
            TypeRef(class_id) => {
                let class_def = ts.class(*class_id);
                class_def.comparable && (self == other || *other == Null)
            }
        }
    }
}

impl<'src> fmt::Display for CheckedType<'src> {
    fn fmt(&self, f: &'_ mut fmt::Formatter<'_>) -> fmt::Result {
        use self::CheckedType::*;
        match self {
            Int => write!(f, "int"),
            Boolean => write!(f, "boolean"),
            Void => write!(f, "void"),
            Null => write!(f, "null"),
            TypeRef(name) => write!(f, "{}", name),
            UnknownType(name) => write!(f, "?{}", name),
            Array(item) => write!(f, "{}[]", item),
        }
    }
}