microcad_lang/eval/statements/
assignment_statement.rs1use crate::eval::*;
5use crate::value::*;
6
7impl Assignment {
8 pub fn type_check(&self, found: Type) -> EvalResult<()> {
10 if let Some(ty) = &self.specified_type {
11 if ty.ty() != found {
12 return Err(EvalError::TypeMismatch {
13 id: self.id.clone(),
14 expected: ty.ty(),
15 found,
16 });
17 }
18 }
19
20 Ok(())
21 }
22}
23
24impl Eval<()> for AssignmentStatement {
25 fn eval(&self, context: &mut EvalContext) -> EvalResult<()> {
26 log::debug!("Evaluating assignment statement:\n{self}");
27 context.grant(self)?;
28
29 let assignment = &self.assignment;
30
31 let new_value: Value = assignment.expression.eval(context)?;
33 if let Err(err) = assignment.type_check(new_value.ty()) {
34 context.error(self, err)?;
35 return Ok(());
36 }
37
38 let new_value = match new_value {
40 Value::Model(model) => {
41 let attributes = self.attribute_list.eval(context)?;
42 model.borrow_mut().attributes = attributes.clone();
43 Value::Model(model)
44 }
45 value => {
46 if !self.attribute_list.is_empty() {
48 context.error(
49 &self.attribute_list,
50 AttributeError::CannotAssignAttribute(
51 self.assignment.expression.to_string(),
52 ),
53 )?;
54 }
55 value
56 }
57 };
58
59 let mut abort = false;
60
61 if let Ok(symbol) = context.lookup(&QualifiedName::from_id(assignment.id.clone())) {
63 let err = symbol.with_def_mut(|def| match def {
64 SymbolDefinition::Constant(_, id, value) => {
65 if value.is_invalid() {
66 *value = new_value.clone();
67 None
68 } else {
69 Some((
70 assignment.id.clone(),
71 EvalError::ValueAlreadyDefined(
72 id.clone(),
73 value.to_string(),
74 id.src_ref(),
75 ),
76 ))
77 }
78 }
79 SymbolDefinition::ConstExpression(..) => {
80 abort = true;
81 None
82 }
83 _ => Some((
84 assignment.id.clone(),
85 EvalError::NotAnLValue(assignment.id.clone()),
86 )),
87 });
88 if let Some((id, err)) = err {
90 context.error(&id, err)?;
91 }
92 }
93
94 if !abort {
95 match assignment.qualifier() {
97 Qualifier::Const => {
98 if context.get_property(&assignment.id).is_ok() {
99 todo!("property with that name exists")
100 }
101
102 let symbol = context.lookup(&assignment.id.clone().into());
103 match symbol {
104 Ok(symbol) => {
105 if let Err(err) = symbol.set_value(new_value) {
106 context.error(self, err)?
107 }
108 }
109 Err(err) => context.error(self, err)?,
110 }
111 }
112 Qualifier::Value => {
113 let result = if context.get_property(&assignment.id).is_ok() {
114 if context.is_init() {
115 context.init_property(assignment.id.clone(), new_value)
116 } else {
117 todo!("property with that name exists")
118 }
119 } else {
120 context.set_local_value(assignment.id.clone(), new_value)
121 };
122 if let Err(err) = result {
123 context.error(self, err)?;
124 }
125 }
126 Qualifier::Prop => {
127 if context.get_local_value(&assignment.id).is_ok() {
128 todo!("local value with that name exists")
129 }
130 if let Err(err) = context.init_property(assignment.id.clone(), new_value) {
131 context.error(self, err)?;
132 }
133 }
134 }
135 }
136
137 Ok(())
138 }
139}