datex_core/compiler/precompiler/
mod.rs

1use crate::stdlib::{cell::RefCell, collections::HashSet, ops::Range, rc::Rc};
2use crate::type_inference::infer_expression_type_detailed_errors;
3use core::str::FromStr;
4use core::unreachable;
5
6pub mod options;
7pub mod precompiled_ast;
8pub mod scope;
9pub mod scope_stack;
10use crate::ast::expressions::{
11    BinaryOperation, DatexExpressionData, Statements, TypeDeclaration,
12    VariableAccess, VariableAssignment, VariableDeclaration, VariableKind,
13};
14use crate::ast::expressions::{
15    DatexExpression, RemoteExecution, TypeDeclarationKind, VariantAccess,
16};
17use crate::ast::resolved_variable::ResolvedVariable;
18use crate::ast::type_expressions::{TypeExpressionData, TypeVariantAccess};
19use crate::types::definition::TypeDefinition;
20use crate::visitor::type_expression::visitable::TypeExpressionVisitResult;
21use crate::{
22    ast::spanned::Spanned,
23    compiler::error::{
24        CompilerError, DetailedCompilerErrors,
25        DetailedCompilerErrorsWithRichAst, ErrorCollector, MaybeAction,
26        SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst,
27        SpannedCompilerError, collect_or_pass_error,
28    },
29    global::operators::{BinaryOperator, binary::ArithmeticOperator},
30    libs::core::CoreLibPointerId,
31    references::type_reference::{NominalTypeDeclaration, TypeReference},
32    values::core_values::r#type::Type,
33    visitor::{
34        VisitAction,
35        expression::{ExpressionVisitor, visitable::ExpressionVisitResult},
36        type_expression::TypeExpressionVisitor,
37    },
38};
39use options::PrecompilerOptions;
40use precompiled_ast::AstMetadata;
41use precompiled_ast::RichAst;
42use precompiled_ast::VariableShape;
43use scope::NewScopeType;
44use scope_stack::PrecompilerScopeStack;
45
46pub struct Precompiler<'a> {
47    ast_metadata: Rc<RefCell<AstMetadata>>,
48    scope_stack: &'a mut PrecompilerScopeStack,
49    collected_errors: Option<DetailedCompilerErrors>,
50    is_first_level_expression: bool,
51}
52
53/// Precompile the AST by resolving variable references and collecting metadata.
54/// Exits early on first error encountered, returning a SpannedCompilerError.
55pub fn precompile_ast_simple_error(
56    ast: DatexExpression,
57    scope_stack: &mut PrecompilerScopeStack,
58    ast_metadata: Rc<RefCell<AstMetadata>>,
59) -> Result<RichAst, SpannedCompilerError> {
60    precompile_ast(
61        ast,
62        scope_stack,
63        ast_metadata,
64        PrecompilerOptions {
65            detailed_errors: false,
66        },
67    )
68    .map_err(|e| {
69        match e {
70            SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst::Simple(
71                error,
72            ) => error,
73            _ => unreachable!(), // because detailed_errors: false
74        }
75    })
76}
77
78/// Precompile the AST by resolving variable references and collecting metadata.
79/// Collects all errors encountered, returning a DetailedCompilerErrorsWithRichAst.
80pub fn precompile_ast_detailed_errors(
81    ast: DatexExpression,
82    scope_stack: &mut PrecompilerScopeStack,
83    ast_metadata: Rc<RefCell<AstMetadata>>,
84) -> Result<RichAst, DetailedCompilerErrorsWithRichAst> {
85    precompile_ast(
86        ast,
87        scope_stack,
88        ast_metadata,
89        PrecompilerOptions {
90            detailed_errors: true,
91        },
92    )
93    .map_err(|e| {
94        match e {
95            SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst::Detailed(
96                error,
97            ) => error,
98            _ => unreachable!(), // because detailed_errors: true
99        }
100    })
101}
102
103/// Precompile the AST by resolving variable references and collecting metadata.
104pub fn precompile_ast(
105    ast: DatexExpression,
106    scope_stack: &mut PrecompilerScopeStack,
107    ast_metadata: Rc<RefCell<AstMetadata>>,
108    options: PrecompilerOptions,
109) -> Result<RichAst, SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst> {
110    Precompiler::new(scope_stack, ast_metadata).precompile(ast, options)
111}
112
113impl<'a> Precompiler<'a> {
114    pub fn new(
115        scope_stack: &'a mut PrecompilerScopeStack,
116        ast_metadata: Rc<RefCell<AstMetadata>>,
117    ) -> Self {
118        Self {
119            ast_metadata,
120            scope_stack,
121            collected_errors: None,
122            is_first_level_expression: true,
123        }
124    }
125
126    /// Collects an error if detailed error collection is enabled,
127    /// or returns the error as Err()
128    fn collect_error(
129        &mut self,
130        error: SpannedCompilerError,
131    ) -> Result<(), SpannedCompilerError> {
132        match &mut self.collected_errors {
133            Some(collected_errors) => {
134                collected_errors.record_error(error);
135                Ok(())
136            }
137            None => Err(error),
138        }
139    }
140
141    /// Collects the Err variant of the Result if detailed error collection is enabled,
142    /// or returns the Result mapped to a MaybeAction.
143    fn collect_result<T>(
144        &mut self,
145        result: Result<T, SpannedCompilerError>,
146    ) -> Result<MaybeAction<T>, SpannedCompilerError> {
147        collect_or_pass_error(&mut self.collected_errors, result)
148    }
149
150    fn get_variable_and_update_metadata(
151        &mut self,
152        name: &str,
153    ) -> Result<usize, CompilerError> {
154        self.scope_stack.get_variable_and_update_metadata(
155            name,
156            &mut self.ast_metadata.borrow_mut(),
157        )
158    }
159
160    /// Precompile the AST by resolving variable references and collecting metadata.
161    fn precompile(
162        mut self,
163        mut ast: DatexExpression,
164        options: PrecompilerOptions,
165    ) -> Result<RichAst, SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst>
166    {
167        if options.detailed_errors {
168            self.collected_errors = Some(DetailedCompilerErrors::default());
169        }
170
171        // Hoist top-level type declaration if any
172        if let DatexExpressionData::TypeDeclaration(type_declaration) =
173            &mut ast.data
174        {
175            self.hoist_variable(type_declaration);
176        }
177
178        // visit ast recursively
179        // returns Error directly if early exit on first error is enabled
180
181        self.visit_datex_expression(&mut ast)?;
182
183        let mut rich_ast = RichAst {
184            metadata: self.ast_metadata,
185            ast,
186        };
187
188        // type inference - currently only if detailed errors are enabled
189        // FIXME #675: always do type inference here, not only for detailed errors
190        if options.detailed_errors {
191            let type_res = infer_expression_type_detailed_errors(&mut rich_ast);
192
193            // append type errors to collected_errors if any
194            if let Some(collected_errors) = self.collected_errors.as_mut()
195                && let Err(type_errors) = type_res
196            {
197                collected_errors.append(type_errors.into());
198            }
199        }
200
201        // if collecting detailed errors and an error occurred, return
202        if let Some(errors) = self.collected_errors
203            && errors.has_errors()
204        {
205            Err(
206                SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst::Detailed(
207                    DetailedCompilerErrorsWithRichAst {
208                        errors,
209                        ast: rich_ast,
210                    },
211                ),
212            )
213        } else {
214            Ok(rich_ast)
215        }
216    }
217
218    /// Adds a new variable to the current scope and metadata
219    /// Returns the new variable ID
220    fn add_new_variable(&mut self, name: String, kind: VariableShape) -> usize {
221        let new_id = self.ast_metadata.borrow().variables.len();
222        let var_metadata =
223            self.scope_stack
224                .add_new_variable(name.clone(), new_id, kind);
225        self.ast_metadata.borrow_mut().variables.push(var_metadata);
226        new_id
227    }
228
229    /// Resolves a variable name to either a local variable ID if it was already declared (or hoisted),
230    /// or to a core library pointer ID if it is a core variable.
231    /// If the variable cannot be resolved, a CompilerError is returned.
232    fn resolve_variable(
233        &mut self,
234        name: &str,
235    ) -> Result<ResolvedVariable, CompilerError> {
236        // If variable exist
237        if let Ok(id) = self.get_variable_and_update_metadata(name) {
238            Ok(ResolvedVariable::VariableId(id))
239        }
240        // try to resolve core variable
241        else if let Ok(core) = CoreLibPointerId::from_str(name) {
242            Ok(ResolvedVariable::PointerAddress(core.into()))
243        } else {
244            Err(CompilerError::UndeclaredVariable(name.to_string()))
245        }
246    }
247
248    fn scope_type_for_expression(
249        &mut self,
250        expr: &DatexExpression,
251    ) -> NewScopeType {
252        match &expr.data {
253            DatexExpressionData::RemoteExecution(_) => NewScopeType::None,
254            _ => NewScopeType::NewScope,
255        }
256    }
257
258    /// Hoist a variable declaration by marking it as hoisted and
259    /// registering it in the current scope and metadata.
260    fn hoist_variable(&mut self, data: &mut TypeDeclaration) {
261        // set hoisted to true
262        data.hoisted = true;
263
264        // register variable
265        let type_id =
266            self.add_new_variable(data.name.clone(), VariableShape::Type);
267
268        let reference = match data.kind {
269            TypeDeclarationKind::Nominal => {
270                Rc::new(RefCell::new(TypeReference::nominal(
271                    Type::UNIT,
272                    NominalTypeDeclaration::from(data.name.clone()),
273                    None,
274                )))
275            }
276            TypeDeclarationKind::Structural => Rc::new(RefCell::new(
277                TypeReference::anonymous(Type::UNIT, None),
278            )),
279        };
280
281        // register placeholder ref in metadata
282        let type_def = Type::new(TypeDefinition::reference(reference), None);
283        {
284            self.ast_metadata
285                .borrow_mut()
286                .variable_metadata_mut(type_id)
287                .expect("TypeDeclaration should have variable metadata")
288                .var_type = Some(type_def.clone());
289        }
290    }
291}
292
293impl<'a> TypeExpressionVisitor<SpannedCompilerError> for Precompiler<'a> {
294    fn visit_literal_type(
295        &mut self,
296        literal: &mut String,
297        span: &Range<usize>,
298    ) -> TypeExpressionVisitResult<SpannedCompilerError> {
299        let resolved_variable = self.resolve_variable(literal)?;
300        Ok(VisitAction::Replace(match resolved_variable {
301            ResolvedVariable::VariableId(id) => {
302                TypeExpressionData::VariableAccess(VariableAccess {
303                    id,
304                    name: literal.to_string(),
305                })
306                .with_span(span.clone())
307            }
308            ResolvedVariable::PointerAddress(pointer_address) => {
309                TypeExpressionData::GetReference(pointer_address)
310                    .with_span(span.clone())
311            }
312        }))
313    }
314    fn visit_variant_access_type(
315        &mut self,
316        variant_access: &mut TypeVariantAccess,
317        span: &Range<usize>,
318    ) -> TypeExpressionVisitResult<SpannedCompilerError> {
319        // ensure lhs exist
320        let _ = self.resolve_variable(&variant_access.name)?;
321        let literal =
322            format!("{}/{}", variant_access.name, variant_access.variant);
323
324        // resolve full variant access
325        let resolved_variable = self.resolve_variable(&literal)?;
326        Ok(VisitAction::Replace(match resolved_variable {
327            ResolvedVariable::VariableId(id) => {
328                TypeExpressionData::VariableAccess(VariableAccess {
329                    id,
330                    name: literal,
331                })
332                .with_span(span.clone())
333            }
334            ResolvedVariable::PointerAddress(pointer_address) => {
335                TypeExpressionData::GetReference(pointer_address)
336                    .with_span(span.clone())
337            }
338        }))
339    }
340}
341impl<'a> ExpressionVisitor<SpannedCompilerError> for Precompiler<'a> {
342    /// Handle expression errors by either recording them if collected_errors is Some,
343    /// or aborting the traversal if collected_errors is None.
344    fn handle_expression_error(
345        &mut self,
346        error: SpannedCompilerError,
347        _expression: &DatexExpression,
348    ) -> Result<VisitAction<DatexExpression>, SpannedCompilerError> {
349        if let Some(collected_errors) = self.collected_errors.as_mut() {
350            collected_errors.record_error(error);
351            Ok(VisitAction::VisitChildren)
352        } else {
353            Err(error)
354        }
355    }
356
357    fn before_visit_datex_expression(&mut self, expr: &mut DatexExpression) {
358        match self.scope_type_for_expression(expr) {
359            NewScopeType::NewScopeWithNewRealm => {
360                self.scope_stack.push_scope();
361                self.scope_stack.increment_realm_index();
362            }
363            NewScopeType::NewScope => {
364                // if in top level scope, don't create a new scope if first ast level
365                if !(self.scope_stack.scopes.len() == 1
366                    && self.is_first_level_expression)
367                {
368                    self.scope_stack.push_scope();
369                }
370            }
371            _ => {}
372        };
373
374        self.is_first_level_expression = false;
375    }
376
377    fn after_visit_datex_expression(&mut self, expr: &mut DatexExpression) {
378        match self.scope_type_for_expression(expr) {
379            NewScopeType::NewScope | NewScopeType::NewScopeWithNewRealm => {
380                // always keep top level scope
381                if self.scope_stack.scopes.len() > 1 {
382                    self.scope_stack.pop_scope();
383                }
384            }
385            _ => {}
386        };
387    }
388
389    fn visit_remote_execution(
390        &mut self,
391        remote_execution: &mut RemoteExecution,
392        _: &Range<usize>,
393    ) -> ExpressionVisitResult<SpannedCompilerError> {
394        self.visit_datex_expression(&mut remote_execution.left)?;
395
396        self.scope_stack.push_scope();
397        self.scope_stack.increment_realm_index();
398
399        self.visit_datex_expression(&mut remote_execution.right)?;
400        self.scope_stack.pop_scope();
401        Ok(VisitAction::SkipChildren)
402    }
403
404    fn visit_statements(
405        &mut self,
406        statements: &mut Statements,
407        _: &Range<usize>,
408    ) -> ExpressionVisitResult<SpannedCompilerError> {
409        let mut registered_names = HashSet::new();
410        for statements in statements.statements.iter_mut() {
411            if let DatexExpressionData::TypeDeclaration(type_declaration) =
412                &mut statements.data
413            {
414                let name = &type_declaration.name;
415                if registered_names.contains(name) {
416                    self.collect_error(
417                        CompilerError::InvalidRedeclaration(name.clone())
418                            .into(),
419                    )?
420                }
421                registered_names.insert(name.clone());
422                self.hoist_variable(type_declaration);
423            }
424        }
425        Ok(VisitAction::VisitChildren)
426    }
427
428    fn visit_type_declaration(
429        &mut self,
430        type_declaration: &mut TypeDeclaration,
431        _: &Range<usize>,
432    ) -> ExpressionVisitResult<SpannedCompilerError> {
433        let name = type_declaration.name.clone();
434        if type_declaration.hoisted {
435            let id = self
436                .get_variable_and_update_metadata(
437                    &type_declaration.name.clone(),
438                )
439                .ok();
440            type_declaration.id = id;
441        } else {
442            type_declaration.id =
443                Some(self.add_new_variable(name, VariableShape::Type));
444        }
445        Ok(VisitAction::VisitChildren)
446    }
447
448    fn visit_binary_operation(
449        &mut self,
450        binary_operation: &mut BinaryOperation,
451        span: &Range<usize>,
452    ) -> ExpressionVisitResult<SpannedCompilerError> {
453        let operator = &binary_operation.operator;
454
455        // handle special case: / operator
456        if operator == &BinaryOperator::Arithmetic(ArithmeticOperator::Divide) {
457            let left = &mut binary_operation.left;
458            let right = &mut binary_operation.right;
459
460            let lit_left =
461                if let DatexExpressionData::Identifier(name) = &left.data {
462                    name.clone()
463                } else {
464                    return Ok(VisitAction::VisitChildren);
465                };
466            let lit_right =
467                if let DatexExpressionData::Identifier(name) = &right.data {
468                    name.clone()
469                } else {
470                    return Ok(VisitAction::VisitChildren);
471                };
472            // both of the sides are identifiers
473            let left_var = self.resolve_variable(lit_left.as_str());
474            let is_right_defined =
475                self.resolve_variable(lit_right.as_str()).is_ok();
476
477            // left is defined (could be integer, or user defined variable)
478            if let Ok(left_var) = left_var {
479                if is_right_defined {
480                    // both sides are defined, left side could be a type, or no,
481                    // same for right side
482                    // could be variant access if the left side is a type and right side does exist as subvariant,
483                    // otherwise we try division
484                    Ok(VisitAction::VisitChildren)
485                } else {
486                    // is right is not defined, fallback to variant access
487                    // could be divison though, where user misspelled rhs (unhandled, will throw)
488                    Ok(VisitAction::Replace(DatexExpression::new(
489                        DatexExpressionData::VariantAccess(VariantAccess {
490                            base: left_var,
491                            name: lit_left,
492                            variant: lit_right,
493                        }),
494                        span.clone(),
495                    )))
496                }
497            } else {
498                Ok(VisitAction::VisitChildren)
499            }
500        } else {
501            Ok(VisitAction::VisitChildren)
502        }
503    }
504
505    fn visit_variable_declaration(
506        &mut self,
507        variable_declaration: &mut VariableDeclaration,
508        span: &Range<usize>,
509    ) -> ExpressionVisitResult<SpannedCompilerError> {
510        // check if variable already declared in active scope
511        if let Some(existing_var_id) = self
512            .scope_stack
513            .get_active_scope()
514            .variable_ids_by_name
515            .get(&variable_declaration.name)
516        {
517            variable_declaration.id = Some(*existing_var_id);
518            return Err(SpannedCompilerError::new_with_span(
519                CompilerError::InvalidRedeclaration(
520                    variable_declaration.name.clone(),
521                ),
522                span.clone(),
523            ));
524        }
525        variable_declaration.id = Some(self.add_new_variable(
526            variable_declaration.name.clone(),
527            VariableShape::Value(variable_declaration.kind),
528        ));
529        Ok(VisitAction::VisitChildren)
530    }
531
532    fn visit_variable_assignment(
533        &mut self,
534        variable_assignment: &mut VariableAssignment,
535        span: &Range<usize>,
536    ) -> ExpressionVisitResult<SpannedCompilerError> {
537        let res = self
538            .get_variable_and_update_metadata(&variable_assignment.name)
539            .map_err(|error| {
540                SpannedCompilerError::new_with_span(error, span.clone())
541            });
542        let action = self.collect_result(res)?;
543        if let MaybeAction::Do(new_id) = action {
544            // continue
545            // check if variable is const
546            let var_shape = self
547                .ast_metadata
548                .borrow()
549                .variable_metadata(new_id)
550                .unwrap()
551                .shape;
552            variable_assignment.id = Some(new_id);
553            if let VariableShape::Value(VariableKind::Const) = var_shape {
554                self.collect_error(SpannedCompilerError::new_with_span(
555                    CompilerError::AssignmentToConst(
556                        variable_assignment.name.clone(),
557                    ),
558                    span.clone(),
559                ))?;
560            };
561        }
562        Ok(VisitAction::VisitChildren)
563    }
564
565    fn visit_identifier(
566        &mut self,
567        identifier: &mut String,
568        span: &Range<usize>,
569    ) -> ExpressionVisitResult<SpannedCompilerError> {
570        let result = self.resolve_variable(identifier).map_err(|error| {
571            SpannedCompilerError::new_with_span(error, span.clone())
572        });
573        let action = self.collect_result(result)?;
574        if let MaybeAction::Do(resolved_variable) = action {
575            return Ok(VisitAction::Replace(match resolved_variable {
576                ResolvedVariable::VariableId(id) => {
577                    DatexExpressionData::VariableAccess(VariableAccess {
578                        id,
579                        name: identifier.clone(),
580                    })
581                    .with_span(span.clone())
582                }
583                ResolvedVariable::PointerAddress(pointer_address) => {
584                    DatexExpressionData::GetReference(pointer_address)
585                        .with_span(span.clone())
586                }
587            }));
588        }
589        Ok(VisitAction::SkipChildren)
590    }
591}
592
593#[cfg(test)]
594mod tests {
595    use super::*;
596    use crate::ast::expressions::{CreateRef, Deref};
597    use crate::ast::resolved_variable::ResolvedVariable;
598    use crate::ast::src_id::SrcId;
599    use crate::ast::type_expressions::{StructuralMap, TypeExpressionData};
600    use crate::parser::Parser;
601    use crate::references::reference::ReferenceMutability;
602    use crate::stdlib::assert_matches::assert_matches;
603    use crate::values::core_values::integer::Integer;
604    use crate::values::pointer::PointerAddress;
605
606    fn precompile(
607        ast: DatexExpression,
608        options: PrecompilerOptions,
609    ) -> Result<RichAst, SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst>
610    {
611        let mut scope_stack = PrecompilerScopeStack::default();
612        let ast_metadata = Rc::new(RefCell::new(AstMetadata::default()));
613        Precompiler::new(&mut scope_stack, ast_metadata)
614            .precompile(ast, options)
615    }
616
617    #[test]
618    fn test_precompiler_visit() {
619        let options = PrecompilerOptions::default();
620        let ast = Parser::parse_with_default_options(
621            "var x: integer = 34; var y = 10; x + y",
622        )
623        .unwrap();
624        let res = precompile(ast, options).unwrap();
625        println!("{:#?}", res.ast);
626    }
627
628    #[test]
629    fn property_access() {
630        let options = PrecompilerOptions::default();
631        let ast =
632            Parser::parse_with_default_options("var x = {a: 1}; x.a").unwrap();
633        precompile(ast, options).expect("Should precompile without errors");
634    }
635
636    #[test]
637    fn property_access_assignment() {
638        let options = PrecompilerOptions::default();
639        let ast =
640            Parser::parse_with_default_options("var x = {a: 1}; x.a = 2;")
641                .unwrap();
642        precompile(ast, options).expect("Should precompile without errors");
643    }
644
645    #[test]
646    fn undeclared_variable_error() {
647        let options = PrecompilerOptions {
648            detailed_errors: true,
649        };
650        let ast = Parser::parse_with_default_options("x + 10").unwrap();
651        let result = precompile(ast, options);
652        println!("{:#?}", result);
653        assert!(result.is_err());
654    }
655
656    #[test]
657    fn duplicate_variable_error() {
658        let options = PrecompilerOptions {
659            detailed_errors: false,
660        };
661        let ast = Parser::parse_with_default_options("var x = 1; var x = 2;")
662            .unwrap();
663        let result = precompile(ast, options);
664        assert_matches!(result.unwrap_err(), SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst::Simple(SpannedCompilerError{span, error: CompilerError::InvalidRedeclaration(name)})  if name == "x");
665    }
666
667    #[test]
668    fn invalid_type_redeclaration() {
669        let src = r#"
670        type A = integer;
671        type A = text; // redeclaration error
672        "#;
673        let ast = Parser::parse_with_default_options(src).unwrap();
674        let result = precompile(ast, PrecompilerOptions::default());
675        assert!(result.is_err());
676        assert_matches!(
677            result,
678            Err(SimpleCompilerErrorOrDetailedCompilerErrorWithRichAst::Simple(SpannedCompilerError{span, error: CompilerError::InvalidRedeclaration(name)})) if name == "A"
679        );
680    }
681
682    fn parse_unwrap(src: &str) -> DatexExpression {
683        let src_id = SrcId::test();
684        Parser::parse_with_default_options(src).unwrap()
685    }
686
687    fn parse_and_precompile_spanned_result(
688        src: &str,
689    ) -> Result<RichAst, SpannedCompilerError> {
690        let mut scope_stack = PrecompilerScopeStack::default();
691        let ast_metadata = Rc::new(RefCell::new(AstMetadata::default()));
692        let ast = Parser::parse_with_default_options(src)?;
693        precompile_ast_simple_error(ast, &mut scope_stack, ast_metadata)
694    }
695
696    fn parse_and_precompile(src: &str) -> Result<RichAst, CompilerError> {
697        parse_and_precompile_spanned_result(src).map_err(|e| e.error)
698    }
699
700    #[test]
701    fn undeclared_variable() {
702        let result = parse_and_precompile_spanned_result("x + 42");
703        assert!(result.is_err());
704        assert_matches!(
705            result,
706            Err(SpannedCompilerError{ error: CompilerError::UndeclaredVariable(var_name), span })
707            if var_name == "x" && span == Some((0..1))
708        );
709    }
710
711    #[test]
712    fn nominal_type_declaration() {
713        let result = parse_and_precompile("type User = {a: integer}; User");
714        assert!(result.is_ok());
715        let rich_ast = result.unwrap();
716        assert_eq!(
717            rich_ast.ast,
718            DatexExpressionData::Statements(Statements::new_unterminated(
719                vec![
720                    DatexExpressionData::TypeDeclaration(TypeDeclaration {
721                        id: Some(0),
722                        name: "User".to_string(),
723                        definition: TypeExpressionData::StructuralMap(
724                            StructuralMap(vec![(
725                                TypeExpressionData::Text("a".to_string())
726                                    .with_default_span(),
727                                TypeExpressionData::GetReference(
728                                    CoreLibPointerId::Integer(None).into()
729                                )
730                                .with_default_span(),
731                            )])
732                        )
733                        .with_default_span(),
734                        hoisted: true,
735                        kind: TypeDeclarationKind::Nominal,
736                    })
737                    .with_default_span(),
738                    DatexExpressionData::VariableAccess(VariableAccess {
739                        id: 0,
740                        name: "User".to_string()
741                    })
742                    .with_default_span()
743                ]
744            ))
745            .with_default_span()
746        );
747        let metadata = rich_ast.metadata.borrow();
748        let var_meta = metadata.variable_metadata(0).unwrap();
749        assert_eq!(var_meta.shape, VariableShape::Type);
750        println!("{:#?}", var_meta);
751    }
752
753    #[test]
754    fn scoped_variable() {
755        let result = parse_and_precompile("(var z = 42;z); z");
756        assert!(result.is_err());
757        assert_matches!(
758            result,
759            Err(CompilerError::UndeclaredVariable(var_name))
760            if var_name == "z"
761        );
762    }
763
764    #[test]
765    fn core_types() {
766        let result = parse_and_precompile("boolean");
767        assert_matches!(
768            result,
769            Ok(
770                RichAst {
771                    ast: DatexExpression { data: DatexExpressionData::GetReference(pointer_id), ..},
772                    ..
773                }
774            ) if pointer_id == CoreLibPointerId::Boolean.into()
775        );
776        let result = parse_and_precompile("integer");
777        assert_matches!(
778            result,
779            Ok(
780                RichAst {
781                    ast: DatexExpression { data: DatexExpressionData::GetReference(pointer_id), ..},
782                    ..
783                }
784            ) if pointer_id == CoreLibPointerId::Integer(None).into()
785        );
786
787        let result = parse_and_precompile("integer/u8");
788        assert_eq!(
789            result.unwrap().ast,
790            DatexExpressionData::VariantAccess(VariantAccess {
791                base: ResolvedVariable::PointerAddress(
792                    CoreLibPointerId::Integer(None).into()
793                ),
794                name: "integer".to_string(),
795                variant: "u8".to_string(),
796            })
797            .with_default_span()
798        );
799    }
800
801    #[test]
802    fn variant_access() {
803        // core type should work
804        let result =
805            parse_and_precompile("integer/u8").expect("Precompilation failed");
806        assert_eq!(
807            result.ast,
808            DatexExpressionData::VariantAccess(VariantAccess {
809                base: ResolvedVariable::PointerAddress(
810                    CoreLibPointerId::Integer(None).into()
811                ),
812                name: "integer".to_string(),
813                variant: "u8".to_string(),
814            })
815            .with_default_span()
816        );
817
818        // invalid variant should work (will error later in type checking)
819        let result = parse_and_precompile("integer/invalid").unwrap();
820        assert_eq!(
821            result.ast,
822            DatexExpressionData::VariantAccess(VariantAccess {
823                base: ResolvedVariable::PointerAddress(
824                    CoreLibPointerId::Integer(None).into()
825                ),
826                name: "integer".to_string(),
827                variant: "invalid".to_string(),
828            })
829            .with_default_span()
830        );
831
832        // unknown type should error
833        let result = parse_and_precompile("invalid/u8");
834        assert_matches!(result, Err(CompilerError::UndeclaredVariable(var_name)) if var_name == "invalid");
835
836        // a variant access without declaring the super type should error
837        let result = parse_and_precompile("type User/admin = {}; User/admin");
838        assert!(result.is_err());
839        assert_matches!(result, Err(CompilerError::UndeclaredVariable(var_name)) if var_name == "User");
840
841        // declared subtype should work
842        let result = parse_and_precompile(
843            "type User = {}; type User/admin = {}; User/admin",
844        );
845        assert!(result.is_ok());
846        let rich_ast = result.unwrap();
847        assert_eq!(
848            rich_ast.ast,
849            DatexExpressionData::Statements(Statements::new_unterminated(
850                vec![
851                    DatexExpressionData::TypeDeclaration(TypeDeclaration {
852                        id: Some(0),
853                        name: "User".to_string(),
854                        definition: TypeExpressionData::StructuralMap(
855                            StructuralMap(vec![])
856                        )
857                        .with_default_span(),
858                        hoisted: true,
859                        kind: TypeDeclarationKind::Nominal,
860                    })
861                    .with_default_span(),
862                    DatexExpressionData::TypeDeclaration(TypeDeclaration {
863                        id: Some(1),
864                        name: "User/admin".to_string(),
865                        definition: TypeExpressionData::StructuralMap(
866                            StructuralMap(vec![])
867                        )
868                        .with_default_span(),
869                        hoisted: true,
870                        kind: TypeDeclarationKind::Nominal
871                    })
872                    .with_default_span(),
873                    DatexExpressionData::VariantAccess(VariantAccess {
874                        base: ResolvedVariable::VariableId(0),
875                        name: "User".to_string(),
876                        variant: "admin".to_string(),
877                    })
878                    .with_default_span()
879                ]
880            ))
881            .with_default_span()
882        );
883
884        // value shall be interpreted as division
885        let result = parse_and_precompile("var a = 42; var b = 69; a/b");
886        assert!(result.is_ok());
887        let statements = if let DatexExpressionData::Statements(stmts) =
888            result.unwrap().ast.data
889        {
890            stmts
891        } else {
892            core::panic!("Expected statements");
893        };
894        assert_eq!(
895            *statements.statements.get(2).unwrap(),
896            DatexExpressionData::BinaryOperation(BinaryOperation {
897                operator: BinaryOperator::Arithmetic(
898                    ArithmeticOperator::Divide
899                ),
900                left: Box::new(
901                    DatexExpressionData::VariableAccess(VariableAccess {
902                        id: 0,
903                        name: "a".to_string()
904                    })
905                    .with_default_span()
906                ),
907                right: Box::new(
908                    DatexExpressionData::VariableAccess(VariableAccess {
909                        id: 1,
910                        name: "b".to_string()
911                    })
912                    .with_default_span()
913                ),
914                ty: None
915            })
916            .with_default_span()
917        );
918
919        // type with value should be interpreted as division
920        let result = parse_and_precompile("var a = 10; type b = 42; a/b");
921        assert!(result.is_ok());
922        let statements = if let DatexExpressionData::Statements(stmts) =
923            result.unwrap().ast.data
924        {
925            stmts
926        } else {
927            core::panic!("Expected statements");
928        };
929        assert_eq!(
930            *statements.statements.get(2).unwrap(),
931            DatexExpressionData::BinaryOperation(BinaryOperation {
932                operator: BinaryOperator::Arithmetic(
933                    ArithmeticOperator::Divide
934                ),
935                left: Box::new(
936                    DatexExpressionData::VariableAccess(VariableAccess {
937                        id: 1,
938                        name: "a".to_string()
939                    })
940                    .with_default_span()
941                ),
942                right: Box::new(
943                    DatexExpressionData::VariableAccess(VariableAccess {
944                        id: 0,
945                        name: "b".to_string()
946                    })
947                    .with_default_span()
948                ),
949                ty: None
950            })
951            .with_default_span()
952        );
953    }
954
955    #[test]
956    fn test_type_declaration_assigment() {
957        let result = parse_and_precompile("type MyInt = 1; var x = MyInt;");
958        assert!(result.is_ok());
959        let rich_ast = result.unwrap();
960        assert_eq!(
961            rich_ast.ast,
962            DatexExpressionData::Statements(Statements::new_terminated(vec![
963                DatexExpressionData::TypeDeclaration(TypeDeclaration {
964                    id: Some(0),
965                    name: "MyInt".to_string(),
966                    definition: TypeExpressionData::Integer(Integer::from(1))
967                        .with_default_span(),
968                    hoisted: true,
969                    kind: TypeDeclarationKind::Nominal
970                })
971                .with_default_span(),
972                DatexExpressionData::VariableDeclaration(VariableDeclaration {
973                    id: Some(1),
974                    kind: VariableKind::Var,
975                    name: "x".to_string(),
976                    // must refer to variable id 0
977                    init_expression: Box::new(
978                        DatexExpressionData::VariableAccess(VariableAccess {
979                            id: 0,
980                            name: "MyInt".to_string()
981                        })
982                        .with_default_span()
983                    ),
984                    type_annotation: None,
985                })
986                .with_default_span(),
987            ]))
988            .with_default_span()
989        )
990    }
991
992    #[test]
993    fn test_type_declaration_hoisted_assigment() {
994        let result = parse_and_precompile("var x = MyInt; type MyInt = 1;");
995        assert!(result.is_ok());
996        let rich_ast = result.unwrap();
997        assert_eq!(
998            rich_ast.ast,
999            DatexExpressionData::Statements(Statements::new_terminated(vec![
1000                DatexExpressionData::VariableDeclaration(VariableDeclaration {
1001                    id: Some(1),
1002                    kind: VariableKind::Var,
1003                    name: "x".to_string(),
1004                    // must refer to variable id 0
1005                    init_expression: Box::new(
1006                        DatexExpressionData::VariableAccess(VariableAccess {
1007                            id: 0,
1008                            name: "MyInt".to_string()
1009                        })
1010                        .with_default_span()
1011                    ),
1012                    type_annotation: None,
1013                })
1014                .with_default_span(),
1015                DatexExpressionData::TypeDeclaration(TypeDeclaration {
1016                    id: Some(0),
1017                    name: "MyInt".to_string(),
1018                    definition: TypeExpressionData::Integer(Integer::from(1))
1019                        .with_default_span(),
1020                    hoisted: true,
1021                    kind: TypeDeclarationKind::Nominal
1022                })
1023                .with_default_span(),
1024            ]))
1025            .with_default_span()
1026        )
1027    }
1028
1029    #[test]
1030    fn test_type_declaration_hoisted_cross_assigment() {
1031        let result = parse_and_precompile("type x = MyInt; type MyInt = x;");
1032        assert!(result.is_ok());
1033        let rich_ast = result.unwrap();
1034        assert_eq!(
1035            rich_ast.ast,
1036            DatexExpressionData::Statements(Statements::new_terminated(vec![
1037                DatexExpressionData::TypeDeclaration(TypeDeclaration {
1038                    id: Some(0),
1039                    name: "x".to_string(),
1040                    definition: TypeExpressionData::VariableAccess(
1041                        VariableAccess {
1042                            id: 1,
1043                            name: "MyInt".to_string()
1044                        }
1045                    )
1046                    .with_default_span(),
1047                    hoisted: true,
1048                    kind: TypeDeclarationKind::Nominal
1049                })
1050                .with_default_span(),
1051                DatexExpressionData::TypeDeclaration(TypeDeclaration {
1052                    id: Some(1),
1053                    name: "MyInt".to_string(),
1054                    definition: TypeExpressionData::VariableAccess(
1055                        VariableAccess {
1056                            id: 0,
1057                            name: "x".to_string()
1058                        }
1059                    )
1060                    .with_default_span(),
1061                    hoisted: true,
1062                    kind: TypeDeclarationKind::Nominal
1063                })
1064                .with_default_span(),
1065            ]))
1066            .with_default_span()
1067        )
1068    }
1069
1070    #[test]
1071    fn test_type_invalid_nested_type_declaration() {
1072        let result = parse_and_precompile(
1073            "type x = NestedVar; (1; type NestedVar = x;)",
1074        );
1075        assert_matches!(result, Err(CompilerError::UndeclaredVariable(var_name)) if var_name == "NestedVar");
1076    }
1077
1078    #[test]
1079    fn test_type_valid_nested_type_declaration() {
1080        let result =
1081            parse_and_precompile("type x = 10; (1; type NestedVar = x;)");
1082        assert!(result.is_ok());
1083        let rich_ast = result.unwrap();
1084        assert_eq!(
1085            rich_ast.ast,
1086            DatexExpressionData::Statements(Statements::new_unterminated(
1087                vec![
1088                    DatexExpressionData::TypeDeclaration(TypeDeclaration {
1089                        id: Some(0),
1090                        name: "x".to_string(),
1091                        definition: TypeExpressionData::Integer(
1092                            Integer::from(10).into()
1093                        )
1094                        .with_default_span(),
1095                        hoisted: true,
1096                        kind: TypeDeclarationKind::Nominal
1097                    })
1098                    .with_default_span(),
1099                    DatexExpressionData::Statements(
1100                        Statements::new_terminated(vec![
1101                            DatexExpressionData::Integer(Integer::from(1))
1102                                .with_default_span(),
1103                            DatexExpressionData::TypeDeclaration(
1104                                TypeDeclaration {
1105                                    id: Some(1),
1106                                    name: "NestedVar".to_string(),
1107                                    definition:
1108                                        TypeExpressionData::VariableAccess(
1109                                            VariableAccess {
1110                                                id: 0,
1111                                                name: "x".to_string()
1112                                            }
1113                                        )
1114                                        .with_default_span(),
1115                                    hoisted: true,
1116                                    kind: TypeDeclarationKind::Nominal
1117                                }
1118                            )
1119                            .with_default_span(),
1120                        ])
1121                    )
1122                    .with_default_span()
1123                ]
1124            ))
1125            .with_default_span()
1126        )
1127    }
1128
1129    #[test]
1130    fn test_core_reference_type() {
1131        let result = parse_and_precompile("type x = integer");
1132        assert!(result.is_ok());
1133        let rich_ast = result.unwrap();
1134        assert_eq!(
1135            rich_ast.ast,
1136            DatexExpressionData::TypeDeclaration(TypeDeclaration {
1137                id: Some(0),
1138                name: "x".to_string(),
1139                definition: TypeExpressionData::GetReference(
1140                    PointerAddress::from(CoreLibPointerId::Integer(None))
1141                )
1142                .with_default_span(),
1143                hoisted: true,
1144                kind: TypeDeclarationKind::Nominal
1145            })
1146            .with_default_span()
1147        );
1148    }
1149
1150    #[test]
1151    fn test_deref() {
1152        let result = parse_and_precompile("const x = &42; *x");
1153        assert!(result.is_ok());
1154        let rich_ast = result.unwrap();
1155        assert_eq!(
1156            rich_ast.ast,
1157            DatexExpressionData::Statements(Statements::new_unterminated(
1158                vec![
1159                    DatexExpressionData::VariableDeclaration(
1160                        VariableDeclaration {
1161                            id: Some(0),
1162                            kind: VariableKind::Const,
1163                            name: "x".to_string(),
1164                            init_expression: Box::new(
1165                                DatexExpressionData::CreateRef(CreateRef {
1166                                    mutability: ReferenceMutability::Immutable,
1167                                    expression: Box::new(
1168                                        DatexExpressionData::Integer(
1169                                            Integer::from(42)
1170                                        )
1171                                        .with_default_span()
1172                                    )
1173                                })
1174                                .with_default_span(),
1175                            ),
1176                            type_annotation: None,
1177                        }
1178                    )
1179                    .with_default_span(),
1180                    DatexExpressionData::Deref(Deref {
1181                        expression: Box::new(
1182                            DatexExpressionData::VariableAccess(
1183                                VariableAccess {
1184                                    id: 0,
1185                                    name: "x".to_string()
1186                                }
1187                            )
1188                            .with_default_span()
1189                        )
1190                    })
1191                    .with_default_span(),
1192                ]
1193            ))
1194            .with_default_span()
1195        );
1196    }
1197}