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 id.clone(),
77 value.to_string(),
78 id.src_ref(),
79 ),
80 ))
81 }
82 }
83 SymbolDef::Assignment(..) => {
84 abort = true;
85 None
86 }
87 _ => Some((
88 assignment.id.clone(),
89 EvalError::NotAnLValue(assignment.id.clone()),
90 )),
91 });
92 if let Some((id, err)) = err {
94 context.error(&id, err)?;
95 }
96 }
97
98 if !abort {
99 match assignment.qualifier() {
101 Qualifier::Const => {
102 if context.get_property(&assignment.id).is_ok() {
103 todo!("property with that name exists")
104 }
105
106 let symbol = context.lookup(&assignment.id.clone().into(), LookupTarget::Value);
107 match symbol {
108 Ok(symbol) => {
109 if let Err(err) = symbol.set_value(new_value) {
110 context.error(self, err)?
111 }
112 }
113 Err(err) => context.error(self, err)?,
114 }
115 }
116 Qualifier::Value => {
117 let result = if context.get_property(&assignment.id).is_ok() {
118 if context.is_init() {
119 context.init_property(assignment.id.clone(), new_value)
120 } else {
121 todo!("property with that name exists")
122 }
123 } else {
124 context.set_local_value(assignment.id.clone(), new_value)
125 };
126 if let Err(err) = result {
127 context.error(self, err)?;
128 }
129 }
130 Qualifier::Prop => {
131 if context.get_local_value(&assignment.id).is_ok() {
132 todo!("local value with that name exists")
133 }
134 if let Err(err) = context.init_property(assignment.id.clone(), new_value) {
135 context.error(self, err)?;
136 }
137 }
138 }
139 }
140
141 Ok(())
142 }
143}
144
145impl Eval<Value> for Assignment {
146 fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
147 self.expression.eval(context)
148 }
149}