rukalang/modules/
qualify.rs

1use std::collections::BTreeSet;
2
3use crate::syntax::ast::TypeRef;
4use crate::syntax::ast::{Block, Expr, MetaBlock, MetaExpr, Program, QuotedCode, Stmt, TypeExpr};
5
6/// Qualify all local declarations and symbol references in one non-entry module.
7pub fn qualify_program_module(program: &mut Program, module_prefix: &str) {
8    let local_function_names = program
9        .functions
10        .iter()
11        .map(|function| function.name.clone())
12        .chain(
13            program
14                .meta_functions
15                .iter()
16                .map(|function| function.name.clone()),
17        )
18        .collect::<BTreeSet<_>>();
19    let local_struct_names = program
20        .structs
21        .iter()
22        .map(|decl| decl.name.clone())
23        .chain(program.enums.iter().map(|decl| decl.name.clone()))
24        .collect::<BTreeSet<_>>();
25    let local_extern_module_names = program
26        .extern_modules
27        .iter()
28        .map(|decl| decl.name.clone())
29        .collect::<BTreeSet<_>>();
30
31    for decl in &mut program.structs {
32        decl.name = qualify_decl_name(module_prefix, decl.name.as_str());
33        for field in &mut decl.fields {
34            qualify_type_expr(
35                &mut field.ty,
36                module_prefix,
37                &local_function_names,
38                &local_struct_names,
39                &local_extern_module_names,
40            );
41        }
42    }
43
44    for decl in &mut program.enums {
45        decl.name = qualify_decl_name(module_prefix, decl.name.as_str());
46        for variant in &mut decl.variants {
47            for payload in &mut variant.payload {
48                qualify_type_expr(
49                    payload,
50                    module_prefix,
51                    &local_function_names,
52                    &local_struct_names,
53                    &local_extern_module_names,
54                );
55            }
56        }
57    }
58
59    for module in &mut program.extern_modules {
60        module.name = qualify_decl_name(module_prefix, module.name.as_str());
61        for function in &mut module.functions {
62            for param in &mut function.params {
63                qualify_type_ref(
64                    &mut param.ty,
65                    module_prefix,
66                    &local_function_names,
67                    &local_struct_names,
68                    &local_extern_module_names,
69                );
70            }
71            qualify_type_ref(
72                &mut function.return_type,
73                module_prefix,
74                &local_function_names,
75                &local_struct_names,
76                &local_extern_module_names,
77            );
78        }
79    }
80
81    for function in &mut program.functions {
82        function.name = qualify_decl_name(module_prefix, function.name.as_str());
83        for param in &mut function.params {
84            qualify_type_ref(
85                &mut param.ty,
86                module_prefix,
87                &local_function_names,
88                &local_struct_names,
89                &local_extern_module_names,
90            );
91        }
92        qualify_type_ref(
93            &mut function.return_type,
94            module_prefix,
95            &local_function_names,
96            &local_struct_names,
97            &local_extern_module_names,
98        );
99        qualify_block(
100            &mut function.body,
101            module_prefix,
102            &local_function_names,
103            &local_struct_names,
104            &local_extern_module_names,
105        );
106    }
107
108    for function in &mut program.meta_functions {
109        function.name = qualify_decl_name(module_prefix, function.name.as_str());
110        for param in &mut function.params {
111            qualify_type_expr(
112                &mut param.ty,
113                module_prefix,
114                &local_function_names,
115                &local_struct_names,
116                &local_extern_module_names,
117            );
118        }
119        qualify_type_expr(
120            &mut function.return_type,
121            module_prefix,
122            &local_function_names,
123            &local_struct_names,
124            &local_extern_module_names,
125        );
126        qualify_meta_block(
127            &mut function.body,
128            module_prefix,
129            &local_function_names,
130            &local_struct_names,
131            &local_extern_module_names,
132        );
133    }
134}
135
136/// Qualify one local declaration name with module prefix.
137fn qualify_decl_name(module_prefix: &str, name: &str) -> String {
138    format!("{module_prefix}::{name}")
139}
140
141/// Qualify one symbol reference according to local declaration sets.
142fn qualify_symbol_name(
143    name: &str,
144    module_prefix: &str,
145    local_function_names: &BTreeSet<String>,
146    local_struct_names: &BTreeSet<String>,
147    local_extern_module_names: &BTreeSet<String>,
148) -> String {
149    if name.contains("::") {
150        let mut parts = name.split("::");
151        if let Some(head) = parts.next()
152            && (local_function_names.contains(head)
153                || local_struct_names.contains(head)
154                || local_extern_module_names.contains(head))
155        {
156            return format!("{module_prefix}::{name}");
157        }
158        return name.to_owned();
159    }
160
161    if local_function_names.contains(name)
162        || local_struct_names.contains(name)
163        || local_extern_module_names.contains(name)
164    {
165        format!("{module_prefix}::{name}")
166    } else {
167        name.to_owned()
168    }
169}
170
171/// Qualify symbol references within one type reference.
172fn qualify_type_ref(
173    ty: &mut TypeRef,
174    module_prefix: &str,
175    local_function_names: &BTreeSet<String>,
176    local_struct_names: &BTreeSet<String>,
177    local_extern_module_names: &BTreeSet<String>,
178) {
179    qualify_type_expr(
180        &mut ty.ty,
181        module_prefix,
182        local_function_names,
183        local_struct_names,
184        local_extern_module_names,
185    );
186}
187
188/// Qualify symbol references within one type expression.
189fn qualify_type_expr(
190    ty: &mut TypeExpr,
191    module_prefix: &str,
192    local_function_names: &BTreeSet<String>,
193    local_struct_names: &BTreeSet<String>,
194    local_extern_module_names: &BTreeSet<String>,
195) {
196    match ty {
197        TypeExpr::Named(name) => {
198            if local_struct_names.contains(name) {
199                *name = qualify_symbol_name(
200                    name,
201                    module_prefix,
202                    local_function_names,
203                    local_struct_names,
204                    local_extern_module_names,
205                );
206            }
207        }
208        TypeExpr::Pointer { item } => qualify_type_expr(
209            item,
210            module_prefix,
211            local_function_names,
212            local_struct_names,
213            local_extern_module_names,
214        ),
215        TypeExpr::Array { item, .. } | TypeExpr::Slice { item } => qualify_type_expr(
216            item,
217            module_prefix,
218            local_function_names,
219            local_struct_names,
220            local_extern_module_names,
221        ),
222        TypeExpr::Tuple(items) => {
223            for item in items {
224                qualify_type_expr(
225                    item,
226                    module_prefix,
227                    local_function_names,
228                    local_struct_names,
229                    local_extern_module_names,
230                );
231            }
232        }
233        TypeExpr::Apply { callee, args } => {
234            if local_struct_names.contains(callee) {
235                *callee = qualify_symbol_name(
236                    callee,
237                    module_prefix,
238                    local_function_names,
239                    local_struct_names,
240                    local_extern_module_names,
241                );
242            }
243            for arg in args {
244                qualify_type_expr(
245                    arg,
246                    module_prefix,
247                    local_function_names,
248                    local_struct_names,
249                    local_extern_module_names,
250                );
251            }
252        }
253        TypeExpr::Struct { fields } | TypeExpr::Union { fields } => {
254            for field in fields {
255                qualify_type_expr(
256                    &mut field.ty,
257                    module_prefix,
258                    local_function_names,
259                    local_struct_names,
260                    local_extern_module_names,
261                );
262            }
263        }
264        TypeExpr::Enum { variants } => {
265            for variant in variants {
266                for payload in &mut variant.payload {
267                    qualify_type_expr(
268                        payload,
269                        module_prefix,
270                        local_function_names,
271                        local_struct_names,
272                        local_extern_module_names,
273                    );
274                }
275            }
276        }
277        TypeExpr::TypeKind => {}
278    }
279}
280
281/// Qualify symbol references within one runtime block.
282fn qualify_block(
283    block: &mut Block,
284    module_prefix: &str,
285    local_function_names: &BTreeSet<String>,
286    local_struct_names: &BTreeSet<String>,
287    local_extern_module_names: &BTreeSet<String>,
288) {
289    for stmt in &mut block.statements {
290        match stmt {
291            Stmt::Let { value, .. } => qualify_expr(
292                value,
293                module_prefix,
294                local_function_names,
295                local_struct_names,
296                local_extern_module_names,
297            ),
298            Stmt::Assign { value, .. } => qualify_expr(
299                value,
300                module_prefix,
301                local_function_names,
302                local_struct_names,
303                local_extern_module_names,
304            ),
305            Stmt::If {
306                condition,
307                then_block,
308                else_block,
309                ..
310            } => {
311                qualify_expr(
312                    condition,
313                    module_prefix,
314                    local_function_names,
315                    local_struct_names,
316                    local_extern_module_names,
317                );
318                qualify_block(
319                    then_block,
320                    module_prefix,
321                    local_function_names,
322                    local_struct_names,
323                    local_extern_module_names,
324                );
325                if let Some(else_block) = else_block {
326                    qualify_block(
327                        else_block,
328                        module_prefix,
329                        local_function_names,
330                        local_struct_names,
331                        local_extern_module_names,
332                    );
333                }
334            }
335            Stmt::For { iterable, body, .. } => {
336                qualify_expr(
337                    iterable,
338                    module_prefix,
339                    local_function_names,
340                    local_struct_names,
341                    local_extern_module_names,
342                );
343                qualify_block(
344                    body,
345                    module_prefix,
346                    local_function_names,
347                    local_struct_names,
348                    local_extern_module_names,
349                );
350            }
351            Stmt::While { condition, body } => {
352                qualify_expr(
353                    condition,
354                    module_prefix,
355                    local_function_names,
356                    local_struct_names,
357                    local_extern_module_names,
358                );
359                qualify_block(
360                    body,
361                    module_prefix,
362                    local_function_names,
363                    local_struct_names,
364                    local_extern_module_names,
365                );
366            }
367            Stmt::Match { value, arms } => {
368                qualify_expr(
369                    value,
370                    module_prefix,
371                    local_function_names,
372                    local_struct_names,
373                    local_extern_module_names,
374                );
375                for arm in arms {
376                    qualify_block(
377                        &mut arm.body,
378                        module_prefix,
379                        local_function_names,
380                        local_struct_names,
381                        local_extern_module_names,
382                    );
383                }
384            }
385            Stmt::Meta { body } => qualify_meta_block(
386                body,
387                module_prefix,
388                local_function_names,
389                local_struct_names,
390                local_extern_module_names,
391            ),
392            Stmt::Expr { expr, .. } => qualify_expr(
393                expr,
394                module_prefix,
395                local_function_names,
396                local_struct_names,
397                local_extern_module_names,
398            ),
399        }
400    }
401}
402
403/// Qualify symbol references within one runtime expression.
404fn qualify_expr(
405    expr: &mut Expr,
406    module_prefix: &str,
407    local_function_names: &BTreeSet<String>,
408    local_struct_names: &BTreeSet<String>,
409    local_extern_module_names: &BTreeSet<String>,
410) {
411    match expr {
412        Expr::Call { callee, args, .. } => {
413            if let Expr::Ident { name, .. } = callee.as_mut() {
414                *name = qualify_symbol_name(
415                    name,
416                    module_prefix,
417                    local_function_names,
418                    local_struct_names,
419                    local_extern_module_names,
420                );
421            } else {
422                qualify_expr(
423                    callee,
424                    module_prefix,
425                    local_function_names,
426                    local_struct_names,
427                    local_extern_module_names,
428                );
429            }
430            for arg in args {
431                qualify_call_arg(
432                    arg,
433                    module_prefix,
434                    local_function_names,
435                    local_struct_names,
436                    local_extern_module_names,
437                );
438            }
439        }
440        Expr::IntrinsicCall { args, .. } => {
441            for arg in args {
442                qualify_call_arg(
443                    arg,
444                    module_prefix,
445                    local_function_names,
446                    local_struct_names,
447                    local_extern_module_names,
448                );
449            }
450        }
451        Expr::Tuple { items, .. } => {
452            for item in items {
453                qualify_expr(
454                    item,
455                    module_prefix,
456                    local_function_names,
457                    local_struct_names,
458                    local_extern_module_names,
459                );
460            }
461        }
462        Expr::StructLit { name, fields, .. } => {
463            *name = qualify_symbol_name(
464                name,
465                module_prefix,
466                local_function_names,
467                local_struct_names,
468                local_extern_module_names,
469            );
470            for field in fields {
471                qualify_expr(
472                    &mut field.value,
473                    module_prefix,
474                    local_function_names,
475                    local_struct_names,
476                    local_extern_module_names,
477                );
478            }
479        }
480        Expr::Field { base, .. } => qualify_expr(
481            base,
482            module_prefix,
483            local_function_names,
484            local_struct_names,
485            local_extern_module_names,
486        ),
487        Expr::Index { base, index } => {
488            qualify_expr(
489                base,
490                module_prefix,
491                local_function_names,
492                local_struct_names,
493                local_extern_module_names,
494            );
495            qualify_expr(
496                index,
497                module_prefix,
498                local_function_names,
499                local_struct_names,
500                local_extern_module_names,
501            );
502        }
503        Expr::SliceRange { base, start, end } => {
504            qualify_expr(
505                base,
506                module_prefix,
507                local_function_names,
508                local_struct_names,
509                local_extern_module_names,
510            );
511            if let Some(start) = start {
512                qualify_expr(
513                    start,
514                    module_prefix,
515                    local_function_names,
516                    local_struct_names,
517                    local_extern_module_names,
518                );
519            }
520            if let Some(end) = end {
521                qualify_expr(
522                    end,
523                    module_prefix,
524                    local_function_names,
525                    local_struct_names,
526                    local_extern_module_names,
527                );
528            }
529        }
530        Expr::Prefix { value, .. } => qualify_expr(
531            value,
532            module_prefix,
533            local_function_names,
534            local_struct_names,
535            local_extern_module_names,
536        ),
537        Expr::Binary { lhs, rhs, .. } => {
538            qualify_expr(
539                lhs,
540                module_prefix,
541                local_function_names,
542                local_struct_names,
543                local_extern_module_names,
544            );
545            qualify_expr(
546                rhs,
547                module_prefix,
548                local_function_names,
549                local_struct_names,
550                local_extern_module_names,
551            );
552        }
553        Expr::Relational { lhs, rhs, .. } => {
554            qualify_expr(
555                lhs,
556                module_prefix,
557                local_function_names,
558                local_struct_names,
559                local_extern_module_names,
560            );
561            qualify_expr(
562                rhs,
563                module_prefix,
564                local_function_names,
565                local_struct_names,
566                local_extern_module_names,
567            );
568        }
569        Expr::Block(block) => qualify_block(
570            block,
571            module_prefix,
572            local_function_names,
573            local_struct_names,
574            local_extern_module_names,
575        ),
576        Expr::Splice(meta) => qualify_meta_expr(
577            meta,
578            module_prefix,
579            local_function_names,
580            local_struct_names,
581            local_extern_module_names,
582        ),
583        Expr::Array { items, .. } => {
584            for item in items {
585                qualify_expr(
586                    item,
587                    module_prefix,
588                    local_function_names,
589                    local_struct_names,
590                    local_extern_module_names,
591                );
592            }
593        }
594        Expr::Ident { .. } | Expr::Int(_) | Expr::Number(_) | Expr::String(_) | Expr::Bool(_) => {}
595    }
596}
597
598/// Qualify symbol references within one runtime call argument.
599fn qualify_call_arg(
600    arg: &mut crate::syntax::ast::CallArg,
601    module_prefix: &str,
602    local_function_names: &BTreeSet<String>,
603    local_struct_names: &BTreeSet<String>,
604    local_extern_module_names: &BTreeSet<String>,
605) {
606    match arg {
607        crate::syntax::ast::CallArg::Expr(expr) => qualify_expr(
608            expr,
609            module_prefix,
610            local_function_names,
611            local_struct_names,
612            local_extern_module_names,
613        ),
614        crate::syntax::ast::CallArg::Spread(expr) => qualify_expr(
615            expr,
616            module_prefix,
617            local_function_names,
618            local_struct_names,
619            local_extern_module_names,
620        ),
621        crate::syntax::ast::CallArg::Type(ty) => qualify_type_expr(
622            ty,
623            module_prefix,
624            local_function_names,
625            local_struct_names,
626            local_extern_module_names,
627        ),
628    }
629}
630
631/// Qualify symbol references within one meta block.
632fn qualify_meta_block(
633    block: &mut MetaBlock,
634    module_prefix: &str,
635    local_function_names: &BTreeSet<String>,
636    local_struct_names: &BTreeSet<String>,
637    local_extern_module_names: &BTreeSet<String>,
638) {
639    for stmt in &mut block.statements {
640        match stmt {
641            crate::syntax::ast::MetaStmt::Let { value, .. } => qualify_meta_expr(
642                value,
643                module_prefix,
644                local_function_names,
645                local_struct_names,
646                local_extern_module_names,
647            ),
648            crate::syntax::ast::MetaStmt::Match { value, arms } => {
649                qualify_meta_expr(
650                    value,
651                    module_prefix,
652                    local_function_names,
653                    local_struct_names,
654                    local_extern_module_names,
655                );
656                for arm in arms {
657                    match &mut arm.pattern {
658                        crate::syntax::ast::MetaMatchPattern::Wildcard => {}
659                        crate::syntax::ast::MetaMatchPattern::Int(_)
660                        | crate::syntax::ast::MetaMatchPattern::Bool(_)
661                        | crate::syntax::ast::MetaMatchPattern::String(_) => {}
662                        crate::syntax::ast::MetaMatchPattern::Quote(quoted) => {
663                            qualify_quoted_code(
664                                quoted,
665                                module_prefix,
666                                local_function_names,
667                                local_struct_names,
668                                local_extern_module_names,
669                            );
670                        }
671                        crate::syntax::ast::MetaMatchPattern::Type(ty) => qualify_type_expr(
672                            ty,
673                            module_prefix,
674                            local_function_names,
675                            local_struct_names,
676                            local_extern_module_names,
677                        ),
678                    }
679                    qualify_meta_expr(
680                        &mut arm.result,
681                        module_prefix,
682                        local_function_names,
683                        local_struct_names,
684                        local_extern_module_names,
685                    );
686                }
687            }
688            crate::syntax::ast::MetaStmt::Meta { body } => qualify_meta_block(
689                body,
690                module_prefix,
691                local_function_names,
692                local_struct_names,
693                local_extern_module_names,
694            ),
695            crate::syntax::ast::MetaStmt::Expr { expr } => qualify_meta_expr(
696                expr,
697                module_prefix,
698                local_function_names,
699                local_struct_names,
700                local_extern_module_names,
701            ),
702        }
703    }
704}
705
706/// Qualify symbol references within one meta expression.
707fn qualify_meta_expr(
708    expr: &mut MetaExpr,
709    module_prefix: &str,
710    local_function_names: &BTreeSet<String>,
711    local_struct_names: &BTreeSet<String>,
712    local_extern_module_names: &BTreeSet<String>,
713) {
714    match expr {
715        MetaExpr::Call { callee, args } => {
716            *callee = qualify_symbol_name(
717                callee,
718                module_prefix,
719                local_function_names,
720                local_struct_names,
721                local_extern_module_names,
722            );
723            for arg in args {
724                qualify_meta_expr(
725                    arg,
726                    module_prefix,
727                    local_function_names,
728                    local_struct_names,
729                    local_extern_module_names,
730                );
731            }
732        }
733        MetaExpr::Type(ty) => qualify_type_expr(
734            ty,
735            module_prefix,
736            local_function_names,
737            local_struct_names,
738            local_extern_module_names,
739        ),
740        MetaExpr::BuildExpr(expr) => qualify_expr(
741            expr,
742            module_prefix,
743            local_function_names,
744            local_struct_names,
745            local_extern_module_names,
746        ),
747        MetaExpr::Quote(quoted) => qualify_quoted_code(
748            quoted,
749            module_prefix,
750            local_function_names,
751            local_struct_names,
752            local_extern_module_names,
753        ),
754        MetaExpr::Splice(inner) => qualify_meta_expr(
755            inner,
756            module_prefix,
757            local_function_names,
758            local_struct_names,
759            local_extern_module_names,
760        ),
761        MetaExpr::Ident(_) | MetaExpr::Int(_) | MetaExpr::Bool(_) | MetaExpr::String(_) => {}
762    }
763}
764
765/// Qualify symbol references within one quoted code fragment.
766fn qualify_quoted_code(
767    quoted: &mut QuotedCode,
768    module_prefix: &str,
769    local_function_names: &BTreeSet<String>,
770    local_struct_names: &BTreeSet<String>,
771    local_extern_module_names: &BTreeSet<String>,
772) {
773    match quoted {
774        QuotedCode::Expr(expr) => qualify_expr(
775            expr,
776            module_prefix,
777            local_function_names,
778            local_struct_names,
779            local_extern_module_names,
780        ),
781        QuotedCode::Type(ty) => qualify_type_expr(
782            ty,
783            module_prefix,
784            local_function_names,
785            local_struct_names,
786            local_extern_module_names,
787        ),
788    }
789}