mica/ll/codegen/
structs.rs

1//! Code generation for structs and struct-related things.
2
3use std::{collections::HashMap, rc::Rc};
4
5use super::{
6    variables::{VariableAllocation, VariablePlace},
7    CodeGenerator, ExpressionResult,
8};
9use crate::ll::{
10    ast::{Ast, NodeId},
11    bytecode::{Opcode, Opr24},
12    error::{LanguageError, LanguageErrorKind},
13};
14
15#[derive(Debug, Default)]
16pub(super) struct StructData {
17    /// Mapping from field names to indices.
18    pub(crate) fields: HashMap<Rc<str>, Opr24>,
19    /// The `self` variable. Used in `@field` lookups.
20    pub(crate) receiver: Option<VariablePlace>,
21}
22
23impl StructData {
24    /// Returns the index of the field with the given name, or creates that field if it doesn't
25    /// exist yet.
26    pub(super) fn get_or_create_field(&mut self, name: &str) -> Result<Opr24, LanguageErrorKind> {
27        if !self.fields.contains_key(name) {
28            let index =
29                Opr24::try_from(self.fields.len()).map_err(|_| LanguageErrorKind::TooManyFields)?;
30            self.fields.insert(Rc::from(name), index);
31            Ok(index)
32        } else {
33            Ok(*self.fields.get(name).unwrap())
34        }
35    }
36
37    /// Returns the index of the field with the given name, or `None` if there is no such field.
38    pub(super) fn get_field(&self, name: &str) -> Option<Opr24> {
39        self.fields.get(name).copied()
40    }
41}
42
43impl<'e> CodeGenerator<'e> {
44    /// Generates code for a field lookup.
45    pub(super) fn generate_field(
46        &mut self,
47        ast: &Ast,
48        node: NodeId,
49    ) -> Result<ExpressionResult, LanguageError> {
50        let (name, _) = ast.node_pair(node);
51        let name = ast.string(name).unwrap();
52        let struct_data = self
53            .struct_data
54            .as_deref()
55            .ok_or_else(|| ast.error(node, LanguageErrorKind::FieldOutsideOfImpl))?;
56        let field_id = struct_data.get_field(name).ok_or_else(|| {
57            ast.error(node, LanguageErrorKind::FieldDoesNotExist(Rc::clone(name)))
58        })?;
59        // Unwrapping is OK here because fields are not allowed outside of functions, and each
60        // function with `StructData` passed in assigns to `receiver` at the start of its
61        // code generation.
62        let receiver = struct_data.receiver.unwrap();
63        self.generate_variable_load(receiver);
64        self.chunk.emit((Opcode::GetField, field_id));
65        Ok(ExpressionResult::Present)
66    }
67
68    /// Generates code for a struct declaration.
69    pub(super) fn generate_struct(
70        &mut self,
71        ast: &Ast,
72        node: NodeId,
73    ) -> Result<ExpressionResult, LanguageError> {
74        let (name, _) = ast.node_pair(node);
75        let name = ast.string(name).unwrap();
76
77        self.chunk.emit(Opcode::CreateType);
78        self.chunk.emit_string(name);
79        let variable = self
80            .create_variable(name, VariableAllocation::Allocate)
81            .map_err(|kind| ast.error(node, kind))?;
82        self.generate_variable_assign(variable);
83
84        Ok(ExpressionResult::Present)
85    }
86}