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