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 self.grant(context)?;
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 model.set_id(assignment.id.clone());
42 let attributes = self.attribute_list.eval(context)?;
43 model.borrow_mut().attributes = attributes.clone();
44 Value::Model(model)
45 }
46 value => {
47 if !self.attribute_list.is_empty() {
49 context.error(
50 &self.attribute_list,
51 AttributeError::CannotAssignAttribute(
52 self.assignment.expression.to_string(),
53 ),
54 )?;
55 }
56 value
57 }
58 };
59
60 let mut abort = false;
61
62 if let Ok(symbol) = context.lookup(
64 &QualifiedName::from_id(assignment.id.clone()),
65 LookupTarget::Value,
66 ) {
67 let err = symbol.with_def_mut(|def| match def {
68 SymbolDef::Constant(_, id, value) => {
69 if value.is_invalid() {
70 *value = new_value.clone();
71 None
72 } else {
73 Some((
74 assignment.id.clone(),
75 EvalError::ValueAlreadyDefined {
76 location: assignment.src_ref(),
77 name: id.clone(),
78 value: value.to_string(),
79 previous_location: id.src_ref(),
80 },
81 ))
82 }
83 }
84 SymbolDef::Assignment(..) => {
85 abort = true;
86 None
87 }
88 _ => Some((
89 assignment.id.clone(),
90 EvalError::NotAnLValue(assignment.id.clone()),
91 )),
92 });
93 if let Some((id, err)) = err {
95 context.error(&id, err)?;
96 }
97 }
98
99 if !abort {
100 match assignment.qualifier() {
102 Qualifier::Const => {
103 if context.get_property(&assignment.id).is_ok() {
104 todo!("property with that name exists")
105 }
106
107 let symbol = context.lookup(&assignment.id.clone().into(), LookupTarget::Value);
108 match symbol {
109 Ok(symbol) => {
110 if let Err(err) = symbol.set_value(new_value) {
111 context.error(self, err)?
112 }
113 }
114 Err(err) => context.error(self, err)?,
115 }
116 }
117 Qualifier::Value => {
118 let result = if context.get_property(&assignment.id).is_ok() {
119 if context.is_init() {
120 context.init_property(assignment.id.clone(), new_value)
121 } else {
122 todo!("property with that name exists")
123 }
124 } else {
125 context.set_local_value(assignment.id.clone(), new_value)
126 };
127 if let Err(err) = result {
128 context.error(self, err)?;
129 }
130 }
131 Qualifier::Prop => {
132 if context.get_local_value(&assignment.id).is_ok() {
133 todo!("local value with that name exists")
134 }
135 if let Err(err) = context.init_property(assignment.id.clone(), new_value) {
136 context.error(self, err)?;
137 }
138 }
139 }
140 }
141
142 Ok(())
143 }
144}
145
146impl Eval<Value> for Assignment {
147 fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
148 self.expression.eval(context)
149 }
150}