1use std::collections::{HashMap, HashSet};
2
3use crate::ast::{
4 BlockStatement, CallArgument, CatchClause, ClassMember, DictEntry, DictKey, Expression,
5 ExpressionStatement, IfStatement, Program, Statement, SwitchIndexEntry, SwitchStatement,
6 TemplatePart, VariableDeclaration, WhileStatement,
7};
8use crate::error::{Result, ZuzuRustError};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum OptimizationLevel {
12 O0,
13 O1,
14 O2,
15 O3,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum OptimizationPass {
20 BlockScopeElision,
21 ConstantFolding,
22 ConstantConditionPruning,
23 UnreachablePruning,
24 RegexCache,
25 IdentifierResolution,
26 TypecheckSkip,
27 OperatorEnum,
28 ChainInline,
29 CollectionPresize,
30 SwitchIndexing,
31 RangeArrayLoopLowering,
32}
33
34const ALL_OPTIMIZATION_PASSES: &[OptimizationPass] = &[
35 OptimizationPass::BlockScopeElision,
36 OptimizationPass::ConstantFolding,
37 OptimizationPass::ConstantConditionPruning,
38 OptimizationPass::UnreachablePruning,
39 OptimizationPass::RegexCache,
40 OptimizationPass::IdentifierResolution,
41 OptimizationPass::TypecheckSkip,
42 OptimizationPass::OperatorEnum,
43 OptimizationPass::ChainInline,
44 OptimizationPass::CollectionPresize,
45 OptimizationPass::SwitchIndexing,
46 OptimizationPass::RangeArrayLoopLowering,
47];
48
49impl OptimizationPass {
50 pub fn name(self) -> &'static str {
51 match self {
52 OptimizationPass::BlockScopeElision => "block-scope-elision",
53 OptimizationPass::ConstantFolding => "constant-folding",
54 OptimizationPass::ConstantConditionPruning => "constant-condition-pruning",
55 OptimizationPass::UnreachablePruning => "unreachable-pruning",
56 OptimizationPass::RegexCache => "regex-cache",
57 OptimizationPass::IdentifierResolution => "identifier-resolution",
58 OptimizationPass::TypecheckSkip => "typecheck-skip",
59 OptimizationPass::OperatorEnum => "operator-enum",
60 OptimizationPass::ChainInline => "chain-inline",
61 OptimizationPass::CollectionPresize => "collection-presize",
62 OptimizationPass::SwitchIndexing => "switch-indexing",
63 OptimizationPass::RangeArrayLoopLowering => "range-array-loop-lowering",
64 }
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct OptimizationOptions {
70 level: OptimizationLevel,
71 enabled: HashSet<OptimizationPass>,
72}
73
74impl Default for OptimizationOptions {
75 fn default() -> Self {
76 Self::for_level(OptimizationLevel::O2)
77 }
78}
79
80impl OptimizationOptions {
81 pub fn for_level(level: OptimizationLevel) -> Self {
82 let mut enabled = HashSet::new();
83 match level {
84 OptimizationLevel::O0 => {}
85 OptimizationLevel::O1 | OptimizationLevel::O2 | OptimizationLevel::O3 => {
86 enabled.insert(OptimizationPass::BlockScopeElision);
87 enabled.insert(OptimizationPass::ConstantFolding);
88 enabled.insert(OptimizationPass::UnreachablePruning);
89 enabled.insert(OptimizationPass::RegexCache);
90 enabled.insert(OptimizationPass::TypecheckSkip);
91 enabled.insert(OptimizationPass::CollectionPresize);
92 }
93 }
94 match level {
95 OptimizationLevel::O2 | OptimizationLevel::O3 => {
96 enabled.insert(OptimizationPass::IdentifierResolution);
97 enabled.insert(OptimizationPass::OperatorEnum);
98 enabled.insert(OptimizationPass::ChainInline);
99 }
100 _ => {}
101 }
102 if matches!(level, OptimizationLevel::O3) {
103 enabled.insert(OptimizationPass::SwitchIndexing);
104 enabled.insert(OptimizationPass::RangeArrayLoopLowering);
105 }
106 Self { level, enabled }
107 }
108
109 pub fn o1() -> Self {
110 Self::for_level(OptimizationLevel::O1)
111 }
112
113 pub fn disabled() -> Self {
114 Self::for_level(OptimizationLevel::O0)
115 }
116
117 pub fn level(&self) -> OptimizationLevel {
118 self.level
119 }
120
121 pub fn enables(&self, pass: OptimizationPass) -> bool {
122 self.enabled.contains(&pass)
123 }
124
125 pub fn is_empty(&self) -> bool {
126 self.enabled.is_empty()
127 }
128
129 pub fn enable(&mut self, name: &str) -> Result<()> {
130 let pass = parse_pass_name(name)?;
131 self.enabled.insert(pass);
132 Ok(())
133 }
134
135 pub fn disable(&mut self, name: &str) -> Result<()> {
136 let pass = parse_pass_name(name)?;
137 self.enabled.remove(&pass);
138 Ok(())
139 }
140}
141
142pub fn parse_level(text: &str) -> Option<OptimizationLevel> {
143 match text {
144 "-o0" | "0" => Some(OptimizationLevel::O0),
145 "-o1" | "1" => Some(OptimizationLevel::O1),
146 "-o2" | "2" => Some(OptimizationLevel::O2),
147 "-o3" | "3" => Some(OptimizationLevel::O3),
148 _ => None,
149 }
150}
151
152pub fn parse_pass_name(name: &str) -> Result<OptimizationPass> {
153 for pass in ALL_OPTIMIZATION_PASSES {
154 if pass.name() == name {
155 return Ok(*pass);
156 }
157 }
158 Err(ZuzuRustError::cli(format!(
159 "unknown optimization pass: {name}"
160 )))
161}
162
163pub fn all_passes() -> &'static [OptimizationPass] {
164 ALL_OPTIMIZATION_PASSES
165}
166
167pub fn optimize_program(program: &mut Program, options: &OptimizationOptions) {
168 if options.is_empty() {
169 return;
170 }
171 let mut optimizer = Optimizer { options };
172 optimizer.optimize_statements(&mut program.statements);
173 if options.enables(OptimizationPass::ConstantFolding) {
174 propagate_constants(&mut program.statements);
175 let mut post_propagation_options = options.clone();
176 post_propagation_options
177 .enabled
178 .remove(&OptimizationPass::ConstantConditionPruning);
179 post_propagation_options
180 .enabled
181 .remove(&OptimizationPass::UnreachablePruning);
182 let mut post_propagation_optimizer = Optimizer {
183 options: &post_propagation_options,
184 };
185 post_propagation_optimizer.optimize_statements(&mut program.statements);
186 }
187 if options.enables(OptimizationPass::IdentifierResolution) {
188 annotate_identifier_depths(program);
189 }
190}
191
192struct Optimizer<'a> {
193 options: &'a OptimizationOptions,
194}
195
196impl Optimizer<'_> {
197 fn optimize_statements(&mut self, statements: &mut Vec<Statement>) {
198 let mut optimized = Vec::with_capacity(statements.len());
199 for mut statement in std::mem::take(statements) {
200 self.optimize_statement(&mut statement);
201 if self
202 .options
203 .enables(OptimizationPass::ConstantConditionPruning)
204 {
205 if let Some(replacement) = self.prune_statement(statement) {
206 for mut replacement in replacement {
207 self.optimize_statement(&mut replacement);
208 optimized.push(replacement);
209 }
210 }
211 } else {
212 optimized.push(statement);
213 }
214 if self.options.enables(OptimizationPass::UnreachablePruning)
215 && optimized.last().map(is_terminal_statement).unwrap_or(false)
216 {
217 break;
218 }
219 }
220 *statements = optimized;
221 }
222
223 fn optimize_statement(&mut self, statement: &mut Statement) {
224 match statement {
225 Statement::Block(block) => self.optimize_block(block),
226 Statement::VariableDeclaration(node) => {
227 if let Some(init) = &mut node.init {
228 self.optimize_expression(init);
229 }
230 if self.options.enables(OptimizationPass::TypecheckSkip)
231 && node.runtime_typecheck_required == Some(false)
232 {
233 node.runtime_typecheck_required = Some(false);
234 }
235 }
236 Statement::VariableUnpackDeclaration(node) => {
237 self.optimize_expression(&mut node.init);
238 for entry in node.pattern.entries_mut() {
239 self.optimize_dict_key(&mut entry.key);
240 if let Some(default_value) = &mut entry.default_value {
241 self.optimize_expression(default_value);
242 }
243 if self.options.enables(OptimizationPass::TypecheckSkip)
244 && entry.runtime_typecheck_required == Some(false)
245 {
246 entry.runtime_typecheck_required = Some(false);
247 }
248 }
249 }
250 Statement::FunctionDeclaration(node) => self.optimize_block(&mut node.body),
251 Statement::ClassDeclaration(node) => {
252 for member in &mut node.body {
253 self.optimize_class_member(member);
254 }
255 }
256 Statement::TraitDeclaration(node) => {
257 for member in &mut node.body {
258 self.optimize_class_member(member);
259 }
260 }
261 Statement::ImportDeclaration(node) => {
262 if let Some(condition) = &mut node.condition {
263 self.optimize_expression(&mut condition.test);
264 }
265 }
266 Statement::IfStatement(node) => {
267 self.optimize_expression(&mut node.test);
268 self.optimize_block(&mut node.consequent);
269 if let Some(alternate) = &mut node.alternate {
270 self.optimize_statement(alternate);
271 }
272 }
273 Statement::WhileStatement(node) => {
274 self.optimize_expression(&mut node.test);
275 self.optimize_block(&mut node.body);
276 }
277 Statement::ForStatement(node) => {
278 self.optimize_expression(&mut node.iterable);
279 self.optimize_block(&mut node.body);
280 if let Some(else_block) = &mut node.else_block {
281 self.optimize_block(else_block);
282 }
283 }
284 Statement::SwitchStatement(node) => self.optimize_switch(node),
285 Statement::TryStatement(node) => {
286 self.optimize_block(&mut node.body);
287 for handler in &mut node.handlers {
288 self.optimize_block(&mut handler.body);
289 }
290 }
291 Statement::ReturnStatement(node) => {
292 if let Some(argument) = &mut node.argument {
293 self.optimize_expression(argument);
294 }
295 }
296 Statement::ThrowStatement(node) => self.optimize_expression(&mut node.argument),
297 Statement::DieStatement(node) => self.optimize_expression(&mut node.argument),
298 Statement::PostfixConditionalStatement(node) => {
299 self.optimize_statement(&mut node.statement);
300 self.optimize_expression(&mut node.test);
301 }
302 Statement::KeywordStatement(node) => {
303 for argument in &mut node.arguments {
304 self.optimize_expression(argument);
305 }
306 }
307 Statement::ExpressionStatement(node) => self.optimize_expression(&mut node.expression),
308 Statement::LoopControlStatement(_) => {}
309 }
310 if self
311 .options
312 .enables(OptimizationPass::RangeArrayLoopLowering)
313 {
314 if let Some(mut lowered) = lower_range_array_loop(statement) {
315 self.optimize_statement(&mut lowered);
316 *statement = lowered;
317 }
318 }
319 }
320
321 fn optimize_class_member(&mut self, member: &mut ClassMember) {
322 match member {
323 ClassMember::Field(field) => {
324 if let Some(default_value) = &mut field.default_value {
325 self.optimize_expression(default_value);
326 }
327 }
328 ClassMember::Method(method) => self.optimize_block(&mut method.body),
329 ClassMember::Class(class) => {
330 for member in &mut class.body {
331 self.optimize_class_member(member);
332 }
333 }
334 ClassMember::Trait(trait_decl) => {
335 for member in &mut trait_decl.body {
336 self.optimize_class_member(member);
337 }
338 }
339 }
340 }
341
342 fn optimize_block(&mut self, block: &mut BlockStatement) {
343 self.optimize_statements(&mut block.statements);
344 if self.options.enables(OptimizationPass::BlockScopeElision) {
345 block.needs_lexical_scope = block_needs_lexical_scope(block);
346 }
347 }
348
349 fn optimize_switch(&mut self, node: &mut SwitchStatement) {
350 self.optimize_expression(&mut node.discriminant);
351 if let Some(comparator) = &mut node.comparator {
352 *comparator = preferred_operator(comparator).to_owned();
353 }
354 for case in &mut node.cases {
355 for operator in &mut case.operators {
356 if let Some(operator) = operator {
357 *operator = preferred_operator(operator).to_owned();
358 }
359 }
360 for value in &mut case.values {
361 self.optimize_expression(value);
362 }
363 self.optimize_statements(&mut case.consequent);
364 }
365 if let Some(default) = &mut node.default {
366 self.optimize_statements(default);
367 }
368 if self.options.enables(OptimizationPass::SwitchIndexing) {
369 node.index = build_switch_index(node);
370 }
371 }
372
373 fn optimize_expression(&mut self, expr: &mut Expression) {
374 match expr {
375 Expression::Unary {
376 operator, argument, ..
377 } => {
378 self.optimize_expression(argument);
379 if self.options.enables(OptimizationPass::OperatorEnum) && operator != "\\" {
380 *operator = preferred_operator(operator).to_owned();
381 }
382 }
383 Expression::Binary {
384 operator,
385 left,
386 right,
387 ..
388 } => {
389 self.optimize_expression(left);
390 self.optimize_expression(right);
391 if self.options.enables(OptimizationPass::OperatorEnum) {
392 *operator = preferred_operator(operator).to_owned();
393 }
394 }
395 Expression::Ternary {
396 test,
397 consequent,
398 alternate,
399 ..
400 } => {
401 self.optimize_expression(test);
402 self.optimize_expression(consequent);
403 self.optimize_expression(alternate);
404 }
405 Expression::DefinedOr { left, right, .. } => {
406 self.optimize_expression(left);
407 self.optimize_expression(right);
408 }
409 Expression::Assignment {
410 operator,
411 left,
412 right,
413 ..
414 } => {
415 self.optimize_expression(left);
416 self.optimize_expression(right);
417 if self.options.enables(OptimizationPass::OperatorEnum) {
418 *operator = preferred_operator(operator).to_owned();
419 }
420 }
421 Expression::Call {
422 callee, arguments, ..
423 } => {
424 self.optimize_expression(callee);
425 for argument in arguments {
426 self.optimize_call_argument(argument);
427 }
428 }
429 Expression::MemberAccess { object, .. } => self.optimize_expression(object),
430 Expression::DynamicMemberCall {
431 object,
432 member,
433 arguments,
434 ..
435 } => {
436 self.optimize_expression(object);
437 self.optimize_expression(member);
438 for argument in arguments {
439 self.optimize_call_argument(argument);
440 }
441 }
442 Expression::Index { object, index, .. } => {
443 self.optimize_expression(object);
444 self.optimize_expression(index);
445 }
446 Expression::Slice {
447 object, start, end, ..
448 } => {
449 self.optimize_expression(object);
450 if let Some(start) = start {
451 self.optimize_expression(start);
452 }
453 if let Some(end) = end {
454 self.optimize_expression(end);
455 }
456 }
457 Expression::DictAccess { object, key, .. } => {
458 self.optimize_expression(object);
459 self.optimize_dict_key(key);
460 }
461 Expression::PostfixUpdate {
462 operator, argument, ..
463 } => {
464 self.optimize_expression(argument);
465 if self.options.enables(OptimizationPass::OperatorEnum) {
466 *operator = preferred_operator(operator).to_owned();
467 }
468 }
469 Expression::Lambda { body, .. } => self.optimize_expression(body),
470 Expression::FunctionExpression { body, .. } => self.optimize_block(body),
471 Expression::LetExpression { init, .. } => {
472 if let Some(init) = init {
473 self.optimize_expression(init);
474 }
475 }
476 Expression::TryExpression { body, handlers, .. } => {
477 self.optimize_block(body);
478 for CatchClause { body, .. } in handlers {
479 self.optimize_block(body);
480 }
481 }
482 Expression::DoExpression { body, .. }
483 | Expression::AwaitExpression { body, .. }
484 | Expression::SpawnExpression { body, .. } => self.optimize_block(body),
485 Expression::ArrayLiteral {
486 elements,
487 capacity_hint,
488 ..
489 } => {
490 for element in &mut *elements {
491 self.optimize_expression(element);
492 }
493 if self.options.enables(OptimizationPass::CollectionPresize) {
494 *capacity_hint = Some(collection_element_capacity_hint(elements));
495 }
496 }
497 Expression::SetLiteral {
498 elements,
499 capacity_hint,
500 ..
501 }
502 | Expression::BagLiteral {
503 elements,
504 capacity_hint,
505 ..
506 } => {
507 for element in &mut *elements {
508 self.optimize_expression(element);
509 }
510 if self.options.enables(OptimizationPass::CollectionPresize) {
511 *capacity_hint = Some(collection_element_capacity_hint(elements));
512 }
513 }
514 Expression::DictLiteral {
515 entries,
516 capacity_hint,
517 ..
518 }
519 | Expression::PairListLiteral {
520 entries,
521 capacity_hint,
522 ..
523 } => {
524 for entry in &mut *entries {
525 self.optimize_dict_entry(entry);
526 }
527 if self.options.enables(OptimizationPass::CollectionPresize) {
528 *capacity_hint = Some(entries.len());
529 }
530 }
531 Expression::TemplateLiteral { parts, .. } => {
532 for part in parts {
533 if let TemplatePart::Expression { expression, .. } = part {
534 self.optimize_expression(expression);
535 }
536 }
537 }
538 Expression::SuperCall { arguments, .. } => {
539 for argument in arguments {
540 self.optimize_call_argument(argument);
541 }
542 }
543 Expression::RegexLiteral {
544 pattern,
545 parts,
546 flags,
547 cache_key,
548 ..
549 } => {
550 for part in parts.iter_mut() {
551 if let TemplatePart::Expression { expression, .. } = part {
552 self.optimize_expression(expression);
553 }
554 }
555 if parts.is_empty() && self.options.enables(OptimizationPass::RegexCache) {
556 *cache_key = Some(regex_cache_key(pattern, flags));
557 }
558 }
559 Expression::Identifier { .. }
560 | Expression::NumberLiteral { .. }
561 | Expression::StringLiteral { .. }
562 | Expression::BinaryStringLiteral { .. }
563 | Expression::BooleanLiteral { .. }
564 | Expression::NullLiteral { .. } => {}
565 }
566 if self.options.enables(OptimizationPass::ChainInline) {
567 if let Some(inlined) = inline_simple_chain(expr) {
568 *expr = inlined;
569 }
570 }
571 if self.options.enables(OptimizationPass::ConstantFolding) {
572 if let Some(folded) = fold_expression(expr) {
573 *expr = folded;
574 }
575 }
576 }
577
578 fn optimize_dict_entry(&mut self, entry: &mut DictEntry) {
579 self.optimize_dict_key(&mut entry.key);
580 self.optimize_expression(&mut entry.value);
581 }
582
583 fn optimize_dict_key(&mut self, key: &mut DictKey) {
584 if let DictKey::Expression { expression, .. } = key {
585 self.optimize_expression(expression);
586 }
587 }
588
589 fn optimize_call_argument(&mut self, argument: &mut CallArgument) {
590 match argument {
591 CallArgument::Positional { value, .. } => self.optimize_expression(value),
592 CallArgument::Spread { value, .. } => self.optimize_expression(value),
593 CallArgument::Named { name, value, .. } => {
594 self.optimize_dict_key(name);
595 self.optimize_expression(value);
596 }
597 }
598 }
599
600 fn prune_statement(&self, statement: Statement) -> Option<Vec<Statement>> {
601 match statement {
602 Statement::IfStatement(node) => match literal_boolean(&node.test) {
603 Some(true) => Some(node.consequent.statements),
604 Some(false) => match node.alternate {
605 Some(alternate) => Some(vec![*alternate]),
606 None => Some(Vec::new()),
607 },
608 None => Some(vec![Statement::IfStatement(node)]),
609 },
610 Statement::WhileStatement(node) if literal_boolean(&node.test) == Some(false) => {
611 Some(Vec::new())
612 }
613 Statement::PostfixConditionalStatement(node) => {
614 let truth = literal_boolean(&node.test)?;
615 let should_run = if node.keyword == "if" { truth } else { !truth };
616 if should_run {
617 Some(vec![*node.statement])
618 } else {
619 Some(Vec::new())
620 }
621 }
622 other => Some(vec![other]),
623 }
624 }
625}
626
627#[derive(Default)]
628struct HereUsage {
629 free: usize,
630 captured: usize,
631 unsupported: bool,
632}
633
634fn inline_simple_chain(expr: &Expression) -> Option<Expression> {
635 let direction = optimized_expression_chain_direction(expr)?;
636 let mut base = None;
637 let mut stages = Vec::new();
638 collect_chain_parts(expr, direction, &mut base, &mut stages)?;
639 let mut current = base?;
640 for stage in stages {
641 let usage = analyse_here_usage(&stage, false);
642 if usage.free != 1 || usage.captured != 0 || usage.unsupported {
643 return None;
644 }
645 current = substitute_here(&stage, ¤t);
646 }
647 Some(current)
648}
649
650fn collect_chain_parts(
651 expr: &Expression,
652 direction: &'static str,
653 base: &mut Option<Expression>,
654 stages: &mut Vec<Expression>,
655) -> Option<()> {
656 match expr {
657 Expression::Binary {
658 operator,
659 left,
660 right,
661 ..
662 } if optimized_chain_direction(operator) == Some(direction) => {
663 if direction == "right" {
664 collect_chain_parts(left, direction, base, stages)?;
665 stages.push((**right).clone());
666 } else {
667 collect_chain_parts(right, direction, base, stages)?;
668 stages.push((**left).clone());
669 }
670 Some(())
671 }
672 Expression::Binary { operator, .. } if optimized_chain_direction(operator).is_some() => {
673 None
674 }
675 other if base.is_none() => {
676 *base = Some(other.clone());
677 Some(())
678 }
679 _ => None,
680 }
681}
682
683fn optimized_chain_direction(operator: &str) -> Option<&'static str> {
684 match operator {
685 "▷" | "|>" => Some("right"),
686 "◁" | "<|" => Some("left"),
687 _ => None,
688 }
689}
690
691fn optimized_expression_chain_direction(expression: &Expression) -> Option<&'static str> {
692 match expression {
693 Expression::Binary { operator, .. } => optimized_chain_direction(operator),
694 _ => None,
695 }
696}
697
698fn analyse_here_usage(expr: &Expression, in_closure: bool) -> HereUsage {
699 let mut usage = HereUsage::default();
700 collect_here_usage(expr, in_closure, &mut usage);
701 usage
702}
703
704fn collect_here_usage(expr: &Expression, in_closure: bool, usage: &mut HereUsage) {
705 match expr {
706 Expression::Identifier { name, .. } if name == "^^" => {
707 if in_closure {
708 usage.captured += 1;
709 } else {
710 usage.free += 1;
711 }
712 }
713 Expression::Unary { argument, .. } => {
714 collect_here_usage(argument, in_closure, usage);
715 }
716 Expression::Binary {
717 operator,
718 left,
719 right,
720 ..
721 } => {
722 if optimized_chain_direction(operator).is_some() {
723 usage.unsupported = true;
724 return;
725 }
726 collect_here_usage(left, in_closure, usage);
727 collect_here_usage(right, in_closure, usage);
728 }
729 Expression::Ternary {
730 test,
731 consequent,
732 alternate,
733 ..
734 } => {
735 collect_here_usage(test, in_closure, usage);
736 collect_here_usage(consequent, in_closure, usage);
737 collect_here_usage(alternate, in_closure, usage);
738 }
739 Expression::DefinedOr { left, right, .. } => {
740 collect_here_usage(left, in_closure, usage);
741 collect_here_usage(right, in_closure, usage);
742 }
743 Expression::Call {
744 callee, arguments, ..
745 } => {
746 collect_here_usage(callee, in_closure, usage);
747 for argument in arguments {
748 collect_call_argument_here_usage(argument, in_closure, usage);
749 }
750 }
751 Expression::MemberAccess { object, .. } => collect_here_usage(object, in_closure, usage),
752 Expression::Index { object, index, .. } => {
753 collect_here_usage(object, in_closure, usage);
754 collect_here_usage(index, in_closure, usage);
755 }
756 Expression::ArrayLiteral { elements, .. } => {
757 for element in elements {
758 collect_here_usage(element, in_closure, usage);
759 }
760 }
761 Expression::Lambda { body, .. } => collect_here_usage(body, true, usage),
762 Expression::FunctionExpression { body, .. } => {
763 if block_contains_here(body) {
764 usage.captured += 1;
765 }
766 }
767 Expression::Identifier { .. }
768 | Expression::NumberLiteral { .. }
769 | Expression::StringLiteral { .. }
770 | Expression::RegexLiteral { .. }
771 | Expression::BooleanLiteral { .. }
772 | Expression::NullLiteral { .. } => {}
773 _ => usage.unsupported = true,
774 }
775}
776
777fn collect_call_argument_here_usage(
778 argument: &CallArgument,
779 in_closure: bool,
780 usage: &mut HereUsage,
781) {
782 match argument {
783 CallArgument::Positional { value, .. } | CallArgument::Spread { value, .. } => {
784 collect_here_usage(value, in_closure, usage);
785 }
786 CallArgument::Named { value, .. } => collect_here_usage(value, in_closure, usage),
787 }
788}
789
790fn block_contains_here(block: &BlockStatement) -> bool {
791 block.statements.iter().any(statement_contains_here)
792}
793
794fn statement_contains_here(statement: &Statement) -> bool {
795 match statement {
796 Statement::Block(block) => block_contains_here(block),
797 Statement::VariableDeclaration(node) => node
798 .init
799 .as_ref()
800 .map(expression_contains_here)
801 .unwrap_or(false),
802 Statement::VariableUnpackDeclaration(node) => expression_contains_here(&node.init),
803 Statement::IfStatement(node) => {
804 expression_contains_here(&node.test)
805 || block_contains_here(&node.consequent)
806 || node
807 .alternate
808 .as_ref()
809 .map(|alternate| statement_contains_here(alternate))
810 .unwrap_or(false)
811 }
812 Statement::WhileStatement(node) => {
813 expression_contains_here(&node.test) || block_contains_here(&node.body)
814 }
815 Statement::ForStatement(node) => {
816 expression_contains_here(&node.iterable) || block_contains_here(&node.body)
817 }
818 Statement::ReturnStatement(node) => node
819 .argument
820 .as_ref()
821 .map(expression_contains_here)
822 .unwrap_or(false),
823 Statement::ThrowStatement(node) => expression_contains_here(&node.argument),
824 Statement::DieStatement(node) => expression_contains_here(&node.argument),
825 Statement::PostfixConditionalStatement(node) => {
826 statement_contains_here(&node.statement) || expression_contains_here(&node.test)
827 }
828 Statement::KeywordStatement(node) => node.arguments.iter().any(expression_contains_here),
829 Statement::ExpressionStatement(node) => expression_contains_here(&node.expression),
830 _ => false,
831 }
832}
833
834fn expression_contains_here(expr: &Expression) -> bool {
835 let usage = analyse_here_usage(expr, false);
836 usage.free > 0 || usage.captured > 0
837}
838
839fn substitute_here(expr: &Expression, replacement: &Expression) -> Expression {
840 match expr {
841 Expression::Identifier { name, .. } if name == "^^" => replacement.clone(),
842 Expression::Unary {
843 line,
844 source_file,
845 operator,
846 argument,
847 traits,
848 inferred_type,
849 } => Expression::Unary {
850 line: *line,
851 source_file: source_file.clone(),
852 operator: operator.clone(),
853 argument: Box::new(substitute_here(argument, replacement)),
854 traits: traits.clone(),
855 inferred_type: inferred_type.clone(),
856 },
857 Expression::Binary {
858 line,
859 source_file,
860 operator,
861 left,
862 right,
863 inferred_type,
864 } => Expression::Binary {
865 line: *line,
866 source_file: source_file.clone(),
867 operator: operator.clone(),
868 left: Box::new(substitute_here(left, replacement)),
869 right: Box::new(substitute_here(right, replacement)),
870 inferred_type: inferred_type.clone(),
871 },
872 Expression::Ternary {
873 line,
874 source_file,
875 test,
876 consequent,
877 alternate,
878 inferred_type,
879 } => Expression::Ternary {
880 line: *line,
881 source_file: source_file.clone(),
882 test: Box::new(substitute_here(test, replacement)),
883 consequent: Box::new(substitute_here(consequent, replacement)),
884 alternate: Box::new(substitute_here(alternate, replacement)),
885 inferred_type: inferred_type.clone(),
886 },
887 Expression::DefinedOr {
888 line,
889 source_file,
890 left,
891 right,
892 inferred_type,
893 } => Expression::DefinedOr {
894 line: *line,
895 source_file: source_file.clone(),
896 left: Box::new(substitute_here(left, replacement)),
897 right: Box::new(substitute_here(right, replacement)),
898 inferred_type: inferred_type.clone(),
899 },
900 Expression::Call {
901 line,
902 source_file,
903 callee,
904 arguments,
905 inferred_type,
906 } => Expression::Call {
907 line: *line,
908 source_file: source_file.clone(),
909 callee: Box::new(substitute_here(callee, replacement)),
910 arguments: arguments
911 .iter()
912 .map(|argument| substitute_call_argument(argument, replacement))
913 .collect(),
914 inferred_type: inferred_type.clone(),
915 },
916 Expression::MemberAccess {
917 line,
918 source_file,
919 object,
920 member,
921 inferred_type,
922 } => Expression::MemberAccess {
923 line: *line,
924 source_file: source_file.clone(),
925 object: Box::new(substitute_here(object, replacement)),
926 member: member.clone(),
927 inferred_type: inferred_type.clone(),
928 },
929 Expression::Index {
930 line,
931 source_file,
932 object,
933 index,
934 inferred_type,
935 } => Expression::Index {
936 line: *line,
937 source_file: source_file.clone(),
938 object: Box::new(substitute_here(object, replacement)),
939 index: Box::new(substitute_here(index, replacement)),
940 inferred_type: inferred_type.clone(),
941 },
942 Expression::ArrayLiteral {
943 line,
944 source_file,
945 elements,
946 capacity_hint,
947 inferred_type,
948 } => Expression::ArrayLiteral {
949 line: *line,
950 source_file: source_file.clone(),
951 elements: elements
952 .iter()
953 .map(|element| substitute_here(element, replacement))
954 .collect(),
955 capacity_hint: *capacity_hint,
956 inferred_type: inferred_type.clone(),
957 },
958 other => other.clone(),
959 }
960}
961
962fn substitute_call_argument(argument: &CallArgument, replacement: &Expression) -> CallArgument {
963 match argument {
964 CallArgument::Positional {
965 line,
966 source_file,
967 value,
968 } => CallArgument::Positional {
969 line: *line,
970 source_file: source_file.clone(),
971 value: substitute_here(value, replacement),
972 },
973 CallArgument::Spread {
974 line,
975 source_file,
976 value,
977 } => CallArgument::Spread {
978 line: *line,
979 source_file: source_file.clone(),
980 value: substitute_here(value, replacement),
981 },
982 CallArgument::Named {
983 line,
984 source_file,
985 name,
986 value,
987 } => CallArgument::Named {
988 line: *line,
989 source_file: source_file.clone(),
990 name: name.clone(),
991 value: substitute_here(value, replacement),
992 },
993 }
994}
995
996fn lower_range_array_loop(statement: &Statement) -> Option<Statement> {
997 let Statement::ForStatement(node) = statement else {
998 return None;
999 };
1000 let (start, end) = range_array_expression(&node.iterable)?;
1001 if !is_simple_range_endpoint(start) || !is_simple_range_endpoint(end) {
1002 return None;
1003 }
1004
1005 let prefix = format!("__zuzu_range_{}_{}", node.line, node.variable);
1006 let current_name = format!("{prefix}_current");
1007 let end_name = format!("{prefix}_end");
1008 let step_name = format!("{prefix}_step");
1009 let seen_name = format!("{prefix}_seen");
1010 let generated = [
1011 current_name.as_str(),
1012 end_name.as_str(),
1013 step_name.as_str(),
1014 seen_name.as_str(),
1015 ];
1016 let mut identifiers = HashSet::new();
1017 collect_statement_identifiers(statement, &mut identifiers);
1018 if generated.iter().any(|name| identifiers.contains(*name)) {
1019 return None;
1020 }
1021
1022 let line = node.line;
1023 let source_file = node.source_file.clone();
1024 let mut statements = vec![
1025 variable_statement(
1026 line,
1027 source_file.clone(),
1028 "let",
1029 ¤t_name,
1030 start.clone(),
1031 ),
1032 variable_statement(line, source_file.clone(), "let", &end_name, end.clone()),
1033 variable_statement(
1034 line,
1035 source_file.clone(),
1036 "let",
1037 &step_name,
1038 Expression::Ternary {
1039 line,
1040 source_file: source_file.clone(),
1041 test: Box::new(binary_expression(
1042 line,
1043 source_file.clone(),
1044 "≤",
1045 identifier_expression(line, source_file.clone(), ¤t_name),
1046 identifier_expression(line, source_file.clone(), &end_name),
1047 )),
1048 consequent: Box::new(number_expression(line, source_file.clone(), "1")),
1049 alternate: Box::new(number_expression(line, source_file.clone(), "-1")),
1050 inferred_type: None,
1051 },
1052 ),
1053 ];
1054
1055 if node.else_block.is_some() {
1056 statements.push(variable_statement(
1057 line,
1058 source_file.clone(),
1059 "let",
1060 &seen_name,
1061 Expression::BooleanLiteral {
1062 line,
1063 source_file: source_file.clone(),
1064 value: false,
1065 inferred_type: None,
1066 },
1067 ));
1068 }
1069
1070 let mut body_statements = Vec::new();
1071 if node.else_block.is_some() {
1072 body_statements.push(expression_statement(
1073 line,
1074 source_file.clone(),
1075 assignment_expression(
1076 line,
1077 source_file.clone(),
1078 identifier_expression(line, source_file.clone(), &seen_name),
1079 Expression::BooleanLiteral {
1080 line,
1081 source_file: source_file.clone(),
1082 value: true,
1083 inferred_type: None,
1084 },
1085 ),
1086 ));
1087 }
1088 body_statements.push(variable_statement(
1089 line,
1090 source_file.clone(),
1091 node.binding_kind.as_deref().unwrap_or("let"),
1092 &node.variable,
1093 identifier_expression(line, source_file.clone(), ¤t_name),
1094 ));
1095 body_statements.push(expression_statement(
1096 line,
1097 source_file.clone(),
1098 Expression::Assignment {
1099 line,
1100 source_file: source_file.clone(),
1101 operator: "+=".to_owned(),
1102 left: Box::new(identifier_expression(
1103 line,
1104 source_file.clone(),
1105 ¤t_name,
1106 )),
1107 right: Box::new(identifier_expression(line, source_file.clone(), &step_name)),
1108 is_weak_write: false,
1109 inferred_type: None,
1110 runtime_typecheck_required: None,
1111 },
1112 ));
1113 body_statements.extend(node.body.statements.clone());
1114
1115 statements.push(Statement::WhileStatement(WhileStatement {
1116 line,
1117 source_file: source_file.clone(),
1118 test: range_loop_test(
1119 line,
1120 source_file.clone(),
1121 ¤t_name,
1122 &end_name,
1123 &step_name,
1124 ),
1125 body: BlockStatement {
1126 line,
1127 source_file: source_file.clone(),
1128 statements: body_statements,
1129 needs_lexical_scope: true,
1130 },
1131 }));
1132
1133 if let Some(else_block) = &node.else_block {
1134 statements.push(Statement::IfStatement(IfStatement {
1135 line,
1136 source_file: source_file.clone(),
1137 test: Expression::Unary {
1138 line,
1139 source_file: source_file.clone(),
1140 operator: "¬".to_owned(),
1141 argument: Box::new(identifier_expression(line, source_file.clone(), &seen_name)),
1142 traits: Vec::new(),
1143 inferred_type: None,
1144 },
1145 consequent: else_block.clone(),
1146 alternate: None,
1147 }));
1148 }
1149
1150 Some(Statement::Block(BlockStatement {
1151 line,
1152 source_file,
1153 statements,
1154 needs_lexical_scope: true,
1155 }))
1156}
1157
1158fn regex_cache_key(pattern: &str, flags: &str) -> String {
1159 format!("{}:{pattern}:{flags}", pattern.len())
1160}
1161
1162fn collection_element_capacity_hint(elements: &[Expression]) -> usize {
1163 elements
1164 .iter()
1165 .map(|element| range_literal_len(element).unwrap_or(1))
1166 .sum()
1167}
1168
1169fn range_literal_len(expression: &Expression) -> Option<usize> {
1170 let Expression::Binary {
1171 operator,
1172 left,
1173 right,
1174 ..
1175 } = expression
1176 else {
1177 return None;
1178 };
1179 if operator != "..." {
1180 return None;
1181 }
1182 let start = literal_number(left)? as i64;
1183 let end = literal_number(right)? as i64;
1184 Some(start.abs_diff(end) as usize + 1)
1185}
1186
1187fn range_array_expression(iterable: &Expression) -> Option<(&Expression, &Expression)> {
1188 let Expression::ArrayLiteral { elements, .. } = iterable else {
1189 return None;
1190 };
1191 let [Expression::Binary {
1192 operator,
1193 left,
1194 right,
1195 ..
1196 }] = elements.as_slice()
1197 else {
1198 return None;
1199 };
1200 if operator != "..." {
1201 return None;
1202 }
1203 Some((left, right))
1204}
1205
1206fn is_simple_range_endpoint(expression: &Expression) -> bool {
1207 matches!(
1208 expression,
1209 Expression::NumberLiteral { .. } | Expression::Identifier { .. }
1210 )
1211}
1212
1213fn range_loop_test(
1214 line: usize,
1215 source_file: Option<String>,
1216 current_name: &str,
1217 end_name: &str,
1218 step_name: &str,
1219) -> Expression {
1220 binary_expression(
1221 line,
1222 source_file.clone(),
1223 "⋁",
1224 binary_expression(
1225 line,
1226 source_file.clone(),
1227 "⋀",
1228 binary_expression(
1229 line,
1230 source_file.clone(),
1231 ">",
1232 identifier_expression(line, source_file.clone(), step_name),
1233 number_expression(line, source_file.clone(), "0"),
1234 ),
1235 binary_expression(
1236 line,
1237 source_file.clone(),
1238 "≤",
1239 identifier_expression(line, source_file.clone(), current_name),
1240 identifier_expression(line, source_file.clone(), end_name),
1241 ),
1242 ),
1243 binary_expression(
1244 line,
1245 source_file.clone(),
1246 "⋀",
1247 binary_expression(
1248 line,
1249 source_file.clone(),
1250 "<",
1251 identifier_expression(line, source_file.clone(), step_name),
1252 number_expression(line, source_file.clone(), "0"),
1253 ),
1254 binary_expression(
1255 line,
1256 source_file.clone(),
1257 "≥",
1258 identifier_expression(line, source_file.clone(), current_name),
1259 identifier_expression(line, source_file, end_name),
1260 ),
1261 ),
1262 )
1263}
1264
1265fn variable_statement(
1266 line: usize,
1267 source_file: Option<String>,
1268 kind: &str,
1269 name: &str,
1270 init: Expression,
1271) -> Statement {
1272 Statement::VariableDeclaration(VariableDeclaration {
1273 line,
1274 source_file,
1275 kind: kind.to_owned(),
1276 declared_type: None,
1277 name: name.to_owned(),
1278 init: Some(init),
1279 is_weak_storage: false,
1280 runtime_typecheck_required: None,
1281 })
1282}
1283
1284fn expression_statement(
1285 line: usize,
1286 source_file: Option<String>,
1287 expression: Expression,
1288) -> Statement {
1289 Statement::ExpressionStatement(ExpressionStatement {
1290 line,
1291 source_file,
1292 expression,
1293 })
1294}
1295
1296fn assignment_expression(
1297 line: usize,
1298 source_file: Option<String>,
1299 left: Expression,
1300 right: Expression,
1301) -> Expression {
1302 Expression::Assignment {
1303 line,
1304 source_file,
1305 operator: ":=".to_owned(),
1306 left: Box::new(left),
1307 right: Box::new(right),
1308 is_weak_write: false,
1309 inferred_type: None,
1310 runtime_typecheck_required: None,
1311 }
1312}
1313
1314fn binary_expression(
1315 line: usize,
1316 source_file: Option<String>,
1317 operator: &str,
1318 left: Expression,
1319 right: Expression,
1320) -> Expression {
1321 Expression::Binary {
1322 line,
1323 source_file,
1324 operator: operator.to_owned(),
1325 left: Box::new(left),
1326 right: Box::new(right),
1327 inferred_type: None,
1328 }
1329}
1330
1331fn identifier_expression(line: usize, source_file: Option<String>, name: &str) -> Expression {
1332 Expression::Identifier {
1333 line,
1334 source_file,
1335 name: name.to_owned(),
1336 inferred_type: None,
1337 binding_depth: None,
1338 }
1339}
1340
1341fn number_expression(line: usize, source_file: Option<String>, value: &str) -> Expression {
1342 Expression::NumberLiteral {
1343 line,
1344 source_file,
1345 value: value.to_owned(),
1346 inferred_type: None,
1347 }
1348}
1349
1350fn collect_statement_identifiers(statement: &Statement, names: &mut HashSet<String>) {
1351 match statement {
1352 Statement::Block(block) => collect_block_identifiers(block, names),
1353 Statement::VariableDeclaration(node) => {
1354 names.insert(node.name.clone());
1355 if let Some(init) = &node.init {
1356 collect_expression_identifiers(init, names);
1357 }
1358 }
1359 Statement::VariableUnpackDeclaration(node) => {
1360 collect_expression_identifiers(&node.init, names);
1361 for entry in node.pattern.entries() {
1362 names.insert(entry.name.clone());
1363 collect_dict_key_identifiers(&entry.key, names);
1364 if let Some(default_value) = &entry.default_value {
1365 collect_expression_identifiers(default_value, names);
1366 }
1367 }
1368 }
1369 Statement::FunctionDeclaration(node) => {
1370 names.insert(node.name.clone());
1371 for param in &node.params {
1372 names.insert(param.name.clone());
1373 if let Some(default_value) = ¶m.default_value {
1374 collect_expression_identifiers(default_value, names);
1375 }
1376 }
1377 collect_block_identifiers(&node.body, names);
1378 }
1379 Statement::ClassDeclaration(node) => {
1380 names.insert(node.name.clone());
1381 for member in &node.body {
1382 collect_class_member_identifiers(member, names);
1383 }
1384 }
1385 Statement::TraitDeclaration(node) => {
1386 names.insert(node.name.clone());
1387 for member in &node.body {
1388 collect_class_member_identifiers(member, names);
1389 }
1390 }
1391 Statement::ImportDeclaration(node) => {
1392 for specifier in &node.specifiers {
1393 names.insert(specifier.local.clone());
1394 }
1395 if let Some(condition) = &node.condition {
1396 collect_expression_identifiers(&condition.test, names);
1397 }
1398 }
1399 Statement::IfStatement(node) => {
1400 collect_expression_identifiers(&node.test, names);
1401 collect_block_identifiers(&node.consequent, names);
1402 if let Some(alternate) = &node.alternate {
1403 collect_statement_identifiers(alternate, names);
1404 }
1405 }
1406 Statement::WhileStatement(node) => {
1407 collect_expression_identifiers(&node.test, names);
1408 collect_block_identifiers(&node.body, names);
1409 }
1410 Statement::ForStatement(node) => {
1411 names.insert(node.variable.clone());
1412 collect_expression_identifiers(&node.iterable, names);
1413 collect_block_identifiers(&node.body, names);
1414 if let Some(else_block) = &node.else_block {
1415 collect_block_identifiers(else_block, names);
1416 }
1417 }
1418 Statement::SwitchStatement(node) => {
1419 collect_expression_identifiers(&node.discriminant, names);
1420 for case in &node.cases {
1421 for value in &case.values {
1422 collect_expression_identifiers(value, names);
1423 }
1424 for statement in &case.consequent {
1425 collect_statement_identifiers(statement, names);
1426 }
1427 }
1428 if let Some(default) = &node.default {
1429 for statement in default {
1430 collect_statement_identifiers(statement, names);
1431 }
1432 }
1433 }
1434 Statement::TryStatement(node) => {
1435 collect_block_identifiers(&node.body, names);
1436 for handler in &node.handlers {
1437 if let Some(name) = handler
1438 .binding
1439 .as_ref()
1440 .and_then(|binding| binding.name.as_ref())
1441 {
1442 names.insert(name.clone());
1443 }
1444 collect_block_identifiers(&handler.body, names);
1445 }
1446 }
1447 Statement::ReturnStatement(node) => {
1448 if let Some(argument) = &node.argument {
1449 collect_expression_identifiers(argument, names);
1450 }
1451 }
1452 Statement::ThrowStatement(node) => collect_expression_identifiers(&node.argument, names),
1453 Statement::DieStatement(node) => collect_expression_identifiers(&node.argument, names),
1454 Statement::PostfixConditionalStatement(node) => {
1455 collect_statement_identifiers(&node.statement, names);
1456 collect_expression_identifiers(&node.test, names);
1457 }
1458 Statement::KeywordStatement(node) => {
1459 for argument in &node.arguments {
1460 collect_expression_identifiers(argument, names);
1461 }
1462 }
1463 Statement::ExpressionStatement(node) => {
1464 collect_expression_identifiers(&node.expression, names);
1465 }
1466 Statement::LoopControlStatement(_) => {}
1467 }
1468}
1469
1470fn collect_block_identifiers(block: &BlockStatement, names: &mut HashSet<String>) {
1471 for statement in &block.statements {
1472 collect_statement_identifiers(statement, names);
1473 }
1474}
1475
1476fn collect_class_member_identifiers(member: &ClassMember, names: &mut HashSet<String>) {
1477 match member {
1478 ClassMember::Field(field) => {
1479 names.insert(field.name.clone());
1480 if let Some(default_value) = &field.default_value {
1481 collect_expression_identifiers(default_value, names);
1482 }
1483 }
1484 ClassMember::Method(method) => {
1485 names.insert(method.name.clone());
1486 for param in &method.params {
1487 names.insert(param.name.clone());
1488 if let Some(default_value) = ¶m.default_value {
1489 collect_expression_identifiers(default_value, names);
1490 }
1491 }
1492 collect_block_identifiers(&method.body, names);
1493 }
1494 ClassMember::Class(class) => {
1495 names.insert(class.name.clone());
1496 for member in &class.body {
1497 collect_class_member_identifiers(member, names);
1498 }
1499 }
1500 ClassMember::Trait(trait_decl) => {
1501 names.insert(trait_decl.name.clone());
1502 for member in &trait_decl.body {
1503 collect_class_member_identifiers(member, names);
1504 }
1505 }
1506 }
1507}
1508
1509fn collect_expression_identifiers(expression: &Expression, names: &mut HashSet<String>) {
1510 match expression {
1511 Expression::Identifier { name, .. } => {
1512 names.insert(name.clone());
1513 }
1514 Expression::Unary { argument, .. } => collect_expression_identifiers(argument, names),
1515 Expression::Binary { left, right, .. }
1516 | Expression::DefinedOr { left, right, .. }
1517 | Expression::Assignment { left, right, .. } => {
1518 collect_expression_identifiers(left, names);
1519 collect_expression_identifiers(right, names);
1520 }
1521 Expression::Ternary {
1522 test,
1523 consequent,
1524 alternate,
1525 ..
1526 } => {
1527 collect_expression_identifiers(test, names);
1528 collect_expression_identifiers(consequent, names);
1529 collect_expression_identifiers(alternate, names);
1530 }
1531 Expression::Call {
1532 callee, arguments, ..
1533 } => {
1534 collect_expression_identifiers(callee, names);
1535 for argument in arguments {
1536 collect_call_argument_identifiers(argument, names);
1537 }
1538 }
1539 Expression::MemberAccess { object, .. } => {
1540 collect_expression_identifiers(object, names);
1541 }
1542 Expression::DynamicMemberCall {
1543 object,
1544 member,
1545 arguments,
1546 ..
1547 } => {
1548 collect_expression_identifiers(object, names);
1549 collect_expression_identifiers(member, names);
1550 for argument in arguments {
1551 collect_call_argument_identifiers(argument, names);
1552 }
1553 }
1554 Expression::Index { object, index, .. } => {
1555 collect_expression_identifiers(object, names);
1556 collect_expression_identifiers(index, names);
1557 }
1558 Expression::Slice {
1559 object, start, end, ..
1560 } => {
1561 collect_expression_identifiers(object, names);
1562 if let Some(start) = start {
1563 collect_expression_identifiers(start, names);
1564 }
1565 if let Some(end) = end {
1566 collect_expression_identifiers(end, names);
1567 }
1568 }
1569 Expression::DictAccess { object, key, .. } => {
1570 collect_expression_identifiers(object, names);
1571 collect_dict_key_identifiers(key, names);
1572 }
1573 Expression::PostfixUpdate { argument, .. } => {
1574 collect_expression_identifiers(argument, names);
1575 }
1576 Expression::Lambda { params, body, .. } => {
1577 for param in params {
1578 names.insert(param.name.clone());
1579 if let Some(default_value) = ¶m.default_value {
1580 collect_expression_identifiers(default_value, names);
1581 }
1582 }
1583 collect_expression_identifiers(body, names);
1584 }
1585 Expression::FunctionExpression { params, body, .. } => {
1586 for param in params {
1587 names.insert(param.name.clone());
1588 if let Some(default_value) = ¶m.default_value {
1589 collect_expression_identifiers(default_value, names);
1590 }
1591 }
1592 collect_block_identifiers(body, names);
1593 }
1594 Expression::LetExpression { name, init, .. } => {
1595 names.insert(name.clone());
1596 if let Some(init) = init {
1597 collect_expression_identifiers(init, names);
1598 }
1599 }
1600 Expression::TryExpression { body, handlers, .. } => {
1601 collect_block_identifiers(body, names);
1602 for handler in handlers {
1603 if let Some(name) = handler
1604 .binding
1605 .as_ref()
1606 .and_then(|binding| binding.name.as_ref())
1607 {
1608 names.insert(name.clone());
1609 }
1610 collect_block_identifiers(&handler.body, names);
1611 }
1612 }
1613 Expression::DoExpression { body, .. }
1614 | Expression::AwaitExpression { body, .. }
1615 | Expression::SpawnExpression { body, .. } => collect_block_identifiers(body, names),
1616 Expression::ArrayLiteral { elements, .. }
1617 | Expression::SetLiteral { elements, .. }
1618 | Expression::BagLiteral { elements, .. } => {
1619 for element in elements {
1620 collect_expression_identifiers(element, names);
1621 }
1622 }
1623 Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
1624 for entry in entries {
1625 collect_dict_key_identifiers(&entry.key, names);
1626 collect_expression_identifiers(&entry.value, names);
1627 }
1628 }
1629 Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
1630 for part in parts {
1631 if let TemplatePart::Expression { expression, .. } = part {
1632 collect_expression_identifiers(expression, names);
1633 }
1634 }
1635 }
1636 Expression::SuperCall { arguments, .. } => {
1637 for argument in arguments {
1638 collect_call_argument_identifiers(argument, names);
1639 }
1640 }
1641 Expression::NumberLiteral { .. }
1642 | Expression::StringLiteral { .. }
1643 | Expression::BinaryStringLiteral { .. }
1644 | Expression::BooleanLiteral { .. }
1645 | Expression::NullLiteral { .. } => {}
1646 }
1647}
1648
1649fn collect_call_argument_identifiers(argument: &CallArgument, names: &mut HashSet<String>) {
1650 match argument {
1651 CallArgument::Positional { value, .. } => collect_expression_identifiers(value, names),
1652 CallArgument::Spread { value, .. } => collect_expression_identifiers(value, names),
1653 CallArgument::Named { name, value, .. } => {
1654 collect_dict_key_identifiers(name, names);
1655 collect_expression_identifiers(value, names);
1656 }
1657 }
1658}
1659
1660fn collect_dict_key_identifiers(key: &DictKey, names: &mut HashSet<String>) {
1661 if let DictKey::Expression { expression, .. } = key {
1662 collect_expression_identifiers(expression, names);
1663 }
1664}
1665
1666fn block_needs_lexical_scope(block: &BlockStatement) -> bool {
1667 block.statements.iter().any(statement_needs_lexical_scope)
1668}
1669
1670fn statement_needs_lexical_scope(statement: &Statement) -> bool {
1671 match statement {
1672 Statement::VariableDeclaration(_)
1673 | Statement::VariableUnpackDeclaration(_)
1674 | Statement::FunctionDeclaration(_)
1675 | Statement::ClassDeclaration(_)
1676 | Statement::TraitDeclaration(_)
1677 | Statement::ImportDeclaration(_) => true,
1678 Statement::ExpressionStatement(node) => expression_declares_binding(&node.expression),
1679 Statement::KeywordStatement(node) => node.arguments.iter().any(expression_declares_binding),
1680 Statement::ReturnStatement(node) => node
1681 .argument
1682 .as_ref()
1683 .map(expression_declares_binding)
1684 .unwrap_or(false),
1685 Statement::ThrowStatement(node) => expression_declares_binding(&node.argument),
1686 Statement::DieStatement(node) => expression_declares_binding(&node.argument),
1687 _ => false,
1688 }
1689}
1690
1691fn expression_declares_binding(expr: &Expression) -> bool {
1692 match expr {
1693 Expression::LetExpression { .. } => true,
1694 Expression::Unary { argument, .. } => expression_declares_binding(argument),
1695 Expression::Binary { left, right, .. }
1696 | Expression::DefinedOr { left, right, .. }
1697 | Expression::Assignment { left, right, .. } => {
1698 expression_declares_binding(left) || expression_declares_binding(right)
1699 }
1700 Expression::Ternary {
1701 test,
1702 consequent,
1703 alternate,
1704 ..
1705 } => {
1706 expression_declares_binding(test)
1707 || expression_declares_binding(consequent)
1708 || expression_declares_binding(alternate)
1709 }
1710 Expression::Call {
1711 callee, arguments, ..
1712 } => {
1713 expression_declares_binding(callee)
1714 || arguments.iter().any(call_argument_declares_binding)
1715 }
1716 Expression::DynamicMemberCall {
1717 object,
1718 member,
1719 arguments,
1720 ..
1721 } => {
1722 expression_declares_binding(object)
1723 || expression_declares_binding(member)
1724 || arguments.iter().any(call_argument_declares_binding)
1725 }
1726 Expression::MemberAccess { object, .. } => expression_declares_binding(object),
1727 Expression::Index { object, index, .. } => {
1728 expression_declares_binding(object) || expression_declares_binding(index)
1729 }
1730 Expression::Slice {
1731 object, start, end, ..
1732 } => {
1733 expression_declares_binding(object)
1734 || start
1735 .as_ref()
1736 .map(|expr| expression_declares_binding(expr))
1737 .unwrap_or(false)
1738 || end
1739 .as_ref()
1740 .map(|expr| expression_declares_binding(expr))
1741 .unwrap_or(false)
1742 }
1743 Expression::DictAccess { object, key, .. } => {
1744 expression_declares_binding(object) || dict_key_declares_binding(key)
1745 }
1746 Expression::PostfixUpdate { argument, .. } => expression_declares_binding(argument),
1747 Expression::Lambda { body, .. } => expression_declares_binding(body),
1748 Expression::FunctionExpression { body, .. }
1749 | Expression::TryExpression { body, .. }
1750 | Expression::DoExpression { body, .. }
1751 | Expression::AwaitExpression { body, .. }
1752 | Expression::SpawnExpression { body, .. } => block_needs_lexical_scope(body),
1753 Expression::ArrayLiteral { elements, .. }
1754 | Expression::SetLiteral { elements, .. }
1755 | Expression::BagLiteral { elements, .. } => {
1756 elements.iter().any(expression_declares_binding)
1757 }
1758 Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
1759 entries.iter().any(|entry| {
1760 dict_key_declares_binding(&entry.key) || expression_declares_binding(&entry.value)
1761 })
1762 }
1763 Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
1764 parts.iter().any(|part| match part {
1765 TemplatePart::Expression { expression, .. } => {
1766 expression_declares_binding(expression)
1767 }
1768 TemplatePart::Text { .. } => false,
1769 })
1770 }
1771 Expression::SuperCall { arguments, .. } => {
1772 arguments.iter().any(call_argument_declares_binding)
1773 }
1774 Expression::Identifier { .. }
1775 | Expression::NumberLiteral { .. }
1776 | Expression::StringLiteral { .. }
1777 | Expression::BinaryStringLiteral { .. }
1778 | Expression::BooleanLiteral { .. }
1779 | Expression::NullLiteral { .. } => false,
1780 }
1781}
1782
1783fn call_argument_declares_binding(argument: &CallArgument) -> bool {
1784 match argument {
1785 CallArgument::Positional { value, .. } => expression_declares_binding(value),
1786 CallArgument::Spread { value, .. } => expression_declares_binding(value),
1787 CallArgument::Named { name, value, .. } => {
1788 dict_key_declares_binding(name) || expression_declares_binding(value)
1789 }
1790 }
1791}
1792
1793fn dict_key_declares_binding(key: &DictKey) -> bool {
1794 match key {
1795 DictKey::Expression { expression, .. } => expression_declares_binding(expression),
1796 DictKey::Identifier { .. } | DictKey::StringLiteral { .. } => false,
1797 }
1798}
1799
1800fn is_terminal_statement(statement: &Statement) -> bool {
1801 matches!(
1802 statement,
1803 Statement::ReturnStatement(_)
1804 | Statement::ThrowStatement(_)
1805 | Statement::DieStatement(_)
1806 | Statement::LoopControlStatement(_)
1807 )
1808}
1809
1810fn fold_expression(expr: &Expression) -> Option<Expression> {
1811 match expr {
1812 Expression::Unary {
1813 line,
1814 source_file,
1815 operator,
1816 argument,
1817 traits: _,
1818 inferred_type,
1819 } => fold_unary(
1820 *line,
1821 source_file.clone(),
1822 operator,
1823 argument,
1824 inferred_type.clone(),
1825 ),
1826 Expression::Binary {
1827 line,
1828 source_file,
1829 operator,
1830 left,
1831 right,
1832 inferred_type,
1833 } => fold_binary(
1834 *line,
1835 source_file.clone(),
1836 operator,
1837 left,
1838 right,
1839 inferred_type.clone(),
1840 ),
1841 Expression::Ternary {
1842 test,
1843 consequent,
1844 alternate,
1845 ..
1846 } => match literal_truth(test)? {
1847 true => Some((**consequent).clone()),
1848 false => Some((**alternate).clone()),
1849 },
1850 Expression::TemplateLiteral {
1851 line,
1852 source_file,
1853 parts,
1854 inferred_type,
1855 } => {
1856 let mut text = String::new();
1857 for part in parts {
1858 match part {
1859 TemplatePart::Text { value, .. } => text.push_str(value),
1860 TemplatePart::Expression { expression, .. } => {
1861 text.push_str(&literal_render(expression)?);
1862 }
1863 }
1864 }
1865 Some(Expression::StringLiteral {
1866 line: *line,
1867 source_file: source_file.clone(),
1868 value: text,
1869 inferred_type: inferred_type.clone(),
1870 })
1871 }
1872 _ => None,
1873 }
1874}
1875
1876fn fold_unary(
1877 line: usize,
1878 source_file: Option<String>,
1879 operator: &str,
1880 argument: &Expression,
1881 inferred_type: Option<String>,
1882) -> Option<Expression> {
1883 match preferred_operator(operator) {
1884 "+" => Some(number_literal(
1885 line,
1886 source_file,
1887 literal_number(argument)?,
1888 inferred_type,
1889 )),
1890 "-" => Some(number_literal(
1891 line,
1892 source_file,
1893 -literal_number(argument)?,
1894 inferred_type,
1895 )),
1896 "¬" | "!" => Some(Expression::BooleanLiteral {
1897 line,
1898 source_file,
1899 value: !literal_truth(argument)?,
1900 inferred_type,
1901 }),
1902 "√" => Some(number_literal(
1903 line,
1904 source_file,
1905 literal_number(argument)?.sqrt(),
1906 inferred_type,
1907 )),
1908 "abs" => Some(number_literal(
1909 line,
1910 source_file,
1911 literal_number(argument)?.abs(),
1912 inferred_type,
1913 )),
1914 "floor" => Some(number_literal(
1915 line,
1916 source_file,
1917 literal_number(argument)?.floor(),
1918 inferred_type,
1919 )),
1920 "ceil" => Some(number_literal(
1921 line,
1922 source_file,
1923 literal_number(argument)?.ceil(),
1924 inferred_type,
1925 )),
1926 "round" => Some(number_literal(
1927 line,
1928 source_file,
1929 literal_number(argument)?.round(),
1930 inferred_type,
1931 )),
1932 "int" => Some(number_literal(
1933 line,
1934 source_file,
1935 literal_number(argument)?.trunc(),
1936 inferred_type,
1937 )),
1938 "uc" => Some(Expression::StringLiteral {
1939 line,
1940 source_file,
1941 value: literal_string(argument)?.to_uppercase(),
1942 inferred_type,
1943 }),
1944 "lc" => Some(Expression::StringLiteral {
1945 line,
1946 source_file,
1947 value: literal_string(argument)?.to_lowercase(),
1948 inferred_type,
1949 }),
1950 "length" => Some(number_literal(
1951 line,
1952 source_file,
1953 literal_string(argument)?.chars().count() as f64,
1954 inferred_type,
1955 )),
1956 _ => None,
1957 }
1958}
1959
1960fn fold_binary(
1961 line: usize,
1962 source_file: Option<String>,
1963 operator: &str,
1964 left: &Expression,
1965 right: &Expression,
1966 inferred_type: Option<String>,
1967) -> Option<Expression> {
1968 let operator = preferred_operator(operator);
1969 match operator {
1970 "+" | "-" | "×" | "÷" | "mod" | "**" => {
1971 let lhs = literal_number(left)?;
1972 let rhs = literal_number(right)?;
1973 let value = match operator {
1974 "+" => lhs + rhs,
1975 "-" => lhs - rhs,
1976 "×" => lhs * rhs,
1977 "÷" => lhs / rhs,
1978 "mod" => lhs % rhs,
1979 "**" => lhs.powf(rhs),
1980 _ => unreachable!(),
1981 };
1982 Some(number_literal(line, source_file, value, inferred_type))
1983 }
1984 "_" => Some(Expression::StringLiteral {
1985 line,
1986 source_file,
1987 value: format!("{}{}", literal_render(left)?, literal_render(right)?),
1988 inferred_type,
1989 }),
1990 "⋀" => Some(Expression::BooleanLiteral {
1991 line,
1992 source_file,
1993 value: literal_truth(left)? && literal_truth(right)?,
1994 inferred_type,
1995 }),
1996 "⋁" => Some(Expression::BooleanLiteral {
1997 line,
1998 source_file,
1999 value: literal_truth(left)? || literal_truth(right)?,
2000 inferred_type,
2001 }),
2002 "⊻" => Some(Expression::BooleanLiteral {
2003 line,
2004 source_file,
2005 value: literal_truth(left)? ^ literal_truth(right)?,
2006 inferred_type,
2007 }),
2008 "⊼" => Some(Expression::BooleanLiteral {
2009 line,
2010 source_file,
2011 value: !(literal_truth(left)? && literal_truth(right)?),
2012 inferred_type,
2013 }),
2014 "=" | "≠" | "<" | ">" | "≤" | "≥" => {
2015 let lhs = literal_number(left)?;
2016 let rhs = literal_number(right)?;
2017 let value = match operator {
2018 "=" => lhs == rhs,
2019 "≠" => lhs != rhs,
2020 "<" => lhs < rhs,
2021 ">" => lhs > rhs,
2022 "≤" => lhs <= rhs,
2023 "≥" => lhs >= rhs,
2024 _ => unreachable!(),
2025 };
2026 Some(Expression::BooleanLiteral {
2027 line,
2028 source_file,
2029 value,
2030 inferred_type,
2031 })
2032 }
2033 "eq" | "ne" | "gt" | "ge" | "lt" | "le" | "eqi" | "nei" | "gti" | "gei" | "lti" | "lei" => {
2034 let mut lhs = literal_render(left)?;
2035 let mut rhs = literal_render(right)?;
2036 if matches!(operator, "eqi" | "nei" | "gti" | "gei" | "lti" | "lei") {
2037 lhs = lhs.to_lowercase();
2038 rhs = rhs.to_lowercase();
2039 }
2040 let value = match operator {
2041 "eq" | "eqi" => lhs == rhs,
2042 "ne" | "nei" => lhs != rhs,
2043 "gt" | "gti" => lhs > rhs,
2044 "ge" | "gei" => lhs >= rhs,
2045 "lt" | "lti" => lhs < rhs,
2046 "le" | "lei" => lhs <= rhs,
2047 _ => unreachable!(),
2048 };
2049 Some(Expression::BooleanLiteral {
2050 line,
2051 source_file,
2052 value,
2053 inferred_type,
2054 })
2055 }
2056 "≡" | "≢" => {
2057 let value = literal_eq(left, right)?;
2058 Some(Expression::BooleanLiteral {
2059 line,
2060 source_file,
2061 value: if operator == "≡" { value } else { !value },
2062 inferred_type,
2063 })
2064 }
2065 "..." => None,
2066 _ => None,
2067 }
2068}
2069
2070fn number_literal(
2071 line: usize,
2072 source_file: Option<String>,
2073 value: f64,
2074 inferred_type: Option<String>,
2075) -> Expression {
2076 Expression::NumberLiteral {
2077 line,
2078 source_file,
2079 value: if value.fract() == 0.0 {
2080 format!("{value:.0}")
2081 } else {
2082 value.to_string()
2083 },
2084 inferred_type,
2085 }
2086}
2087
2088fn literal_number(expr: &Expression) -> Option<f64> {
2089 match expr {
2090 Expression::NumberLiteral { value, .. } => value.parse().ok(),
2091 _ => None,
2092 }
2093}
2094
2095fn literal_string(expr: &Expression) -> Option<&str> {
2096 match expr {
2097 Expression::StringLiteral { value, .. } => Some(value),
2098 _ => None,
2099 }
2100}
2101
2102fn literal_truth(expr: &Expression) -> Option<bool> {
2103 match expr {
2104 Expression::BooleanLiteral { value, .. } => Some(*value),
2105 Expression::NullLiteral { .. } => Some(false),
2106 Expression::NumberLiteral { value, .. } => Some(value.parse::<f64>().ok()? != 0.0),
2107 Expression::StringLiteral { value, .. } => Some(!value.is_empty()),
2108 _ => None,
2109 }
2110}
2111
2112fn literal_boolean(expr: &Expression) -> Option<bool> {
2113 match expr {
2114 Expression::BooleanLiteral { value, .. } => Some(*value),
2115 _ => None,
2116 }
2117}
2118
2119fn literal_render(expr: &Expression) -> Option<String> {
2120 match expr {
2121 Expression::StringLiteral { value, .. } => Some(value.clone()),
2122 Expression::NumberLiteral { value, .. } => Some(value.clone()),
2123 Expression::BooleanLiteral { value, .. } => {
2124 Some(if *value { "true" } else { "false" }.to_owned())
2125 }
2126 Expression::NullLiteral { .. } => Some(String::new()),
2127 _ => None,
2128 }
2129}
2130
2131fn literal_eq(left: &Expression, right: &Expression) -> Option<bool> {
2132 match (left, right) {
2133 (Expression::NullLiteral { .. }, Expression::NullLiteral { .. }) => Some(true),
2134 (
2135 Expression::BooleanLiteral { value: lhs, .. },
2136 Expression::BooleanLiteral { value: rhs, .. },
2137 ) => Some(lhs == rhs),
2138 (
2139 Expression::NumberLiteral { value: lhs, .. },
2140 Expression::NumberLiteral { value: rhs, .. },
2141 ) => Some(lhs.parse::<f64>().ok()? == rhs.parse::<f64>().ok()?),
2142 (
2143 Expression::StringLiteral { value: lhs, .. },
2144 Expression::StringLiteral { value: rhs, .. },
2145 ) => Some(lhs == rhs),
2146 _ => None,
2147 }
2148}
2149
2150pub fn preferred_operator(operator: &str) -> &str {
2151 match operator {
2152 "sqrt" => "√",
2153 "*" => "×",
2154 "/" => "÷",
2155 "<=" => "≤",
2156 ">=" => "≥",
2157 "<=>" | "≷" => "≶",
2158 "==" => "≡",
2159 "!=" => "≢",
2160 "not" => "¬",
2161 "and" => "⋀",
2162 "and?" => "⋀?",
2163 "nand" => "⊼",
2164 "nand?" => "⊼?",
2165 "nor" => "⊽",
2166 "nor?" => "⊽?",
2167 "xor" => "⊻",
2168 "xor?" => "⊻?",
2169 "xnor" => "↔",
2170 "xnor?" => "↔?",
2171 "or" => "⋁",
2172 "or?" => "⋁?",
2173 "onlyif" => "⊨",
2174 "onlyif?" => "⊨?",
2175 "butnot" => "⊭",
2176 "butnot?" => "⊭?",
2177 "union" => "⋃",
2178 "intersection" => "⋂",
2179 "\\" => "∖",
2180 "in" => "∈",
2181 "subsetof" => "⊂",
2182 "supersetof" => "⊃",
2183 "equivalentof" => "⊂⊃",
2184 "|>" => "▷",
2185 "<|" => "◁",
2186 "*=" => "×=",
2187 "/=" => "÷=",
2188 other => other,
2189 }
2190}
2191
2192fn build_switch_index(node: &SwitchStatement) -> Option<Vec<SwitchIndexEntry>> {
2193 let comparator = node.comparator.as_deref().map(preferred_operator);
2194 let mut entries = Vec::new();
2195 let mut seen = HashSet::new();
2196 for (case_index, case) in node.cases.iter().enumerate() {
2197 for (value_index, value) in case.values.iter().enumerate() {
2198 let operator = case
2199 .operators
2200 .get(value_index)
2201 .and_then(|value| value.as_deref())
2202 .map(preferred_operator)
2203 .or(comparator);
2204 let key = switch_key(operator, value)?;
2205 if !seen.insert(key.clone()) {
2206 return None;
2207 }
2208 entries.push(SwitchIndexEntry { key, case_index });
2209 }
2210 }
2211 if entries.is_empty() {
2212 None
2213 } else {
2214 Some(entries)
2215 }
2216}
2217
2218fn switch_key(operator: Option<&str>, expr: &Expression) -> Option<String> {
2219 match operator {
2220 None | Some("≡") => switch_strict_key(expr),
2221 Some("=") => literal_integer(expr).map(|value| format!("i:{value}")),
2222 Some("eq") => match expr {
2223 Expression::StringLiteral { value, .. } => Some(format!("q:{value}")),
2224 _ => None,
2225 },
2226 Some("eqi") => match expr {
2227 Expression::StringLiteral { value, .. } => Some(format!("qi:{}", value.to_lowercase())),
2228 _ => None,
2229 },
2230 _ => None,
2231 }
2232}
2233
2234fn switch_strict_key(expr: &Expression) -> Option<String> {
2235 match expr {
2236 Expression::NullLiteral { .. } => Some("n:".to_owned()),
2237 Expression::BooleanLiteral { value, .. } => Some(format!("b:{value}")),
2238 Expression::NumberLiteral { value, .. } => {
2239 Some(format!("f:{}", value.parse::<f64>().ok()?))
2240 }
2241 Expression::StringLiteral { value, .. } => Some(format!("s:{value}")),
2242 _ => None,
2243 }
2244}
2245
2246fn literal_integer(expr: &Expression) -> Option<i64> {
2247 match expr {
2248 Expression::NumberLiteral { value, .. } => {
2249 let value = value.parse::<f64>().ok()?;
2250 if value.is_finite() && value.fract() == 0.0 {
2251 Some(value as i64)
2252 } else {
2253 None
2254 }
2255 }
2256 _ => None,
2257 }
2258}
2259
2260fn propagate_constants(statements: &mut [Statement]) {
2261 let mut scopes = vec![HashMap::<String, Expression>::new()];
2262 propagate_constants_statements(statements, &mut scopes);
2263}
2264
2265fn propagate_constants_statements(
2266 statements: &mut [Statement],
2267 scopes: &mut Vec<HashMap<String, Expression>>,
2268) {
2269 for statement in statements {
2270 propagate_constants_statement(statement, scopes);
2271 record_constant_declaration(statement, scopes);
2272 }
2273}
2274
2275fn propagate_constants_statement(
2276 statement: &mut Statement,
2277 scopes: &mut Vec<HashMap<String, Expression>>,
2278) {
2279 match statement {
2280 Statement::Block(block) => propagate_constants_block(block, scopes),
2281 Statement::VariableDeclaration(node) => {
2282 if let Some(init) = &mut node.init {
2283 propagate_constants_expression(init, scopes, false);
2284 }
2285 }
2286 Statement::VariableUnpackDeclaration(node) => {
2287 propagate_constants_expression(&mut node.init, scopes, false);
2288 for entry in node.pattern.entries_mut() {
2289 propagate_constants_dict_key(&mut entry.key, scopes);
2290 if let Some(default_value) = &mut entry.default_value {
2291 propagate_constants_expression(default_value, scopes, false);
2292 }
2293 }
2294 }
2295 Statement::FunctionDeclaration(node) => {
2296 forget_assigned_in_block(scopes, &node.body);
2297 }
2298 Statement::ClassDeclaration(node) => {
2299 for member in &mut node.body {
2300 propagate_constants_class_member(member);
2301 }
2302 }
2303 Statement::TraitDeclaration(node) => {
2304 for member in &mut node.body {
2305 propagate_constants_class_member(member);
2306 }
2307 }
2308 Statement::ImportDeclaration(node) => {
2309 if let Some(condition) = &mut node.condition {
2310 propagate_constants_expression(&mut condition.test, scopes, false);
2311 }
2312 }
2313 Statement::IfStatement(node) => {
2314 propagate_constants_expression(&mut node.test, scopes, false);
2315 let mut consequent_scopes = scopes.clone();
2316 propagate_constants_block(&mut node.consequent, &mut consequent_scopes);
2317 if let Some(alternate) = &mut node.alternate {
2318 let mut alternate_scopes = scopes.clone();
2319 propagate_constants_statement(alternate, &mut alternate_scopes);
2320 }
2321 forget_assigned_in_block(scopes, &node.consequent);
2322 if let Some(alternate) = &node.alternate {
2323 forget_assigned_in_statement(scopes, alternate);
2324 }
2325 }
2326 Statement::WhileStatement(node) => {
2327 let mut body_scopes = scopes.clone();
2328 forget_assigned_in_block(&mut body_scopes, &node.body);
2329 propagate_constants_expression(&mut node.test, &mut body_scopes, false);
2330 propagate_constants_block(&mut node.body, &mut body_scopes);
2331 forget_assigned_in_block(scopes, &node.body);
2332 }
2333 Statement::ForStatement(node) => {
2334 propagate_constants_expression(&mut node.iterable, scopes, false);
2335 let mut body_scopes = scopes.clone();
2336 body_scopes.push(HashMap::new());
2337 forget_assigned_in_block(&mut body_scopes, &node.body);
2338 forget_constant(&mut body_scopes, &node.variable);
2339 propagate_constants_statements(&mut node.body.statements, &mut body_scopes);
2340 if let Some(else_block) = &mut node.else_block {
2341 let mut else_scopes = scopes.clone();
2342 propagate_constants_block(else_block, &mut else_scopes);
2343 }
2344 forget_assigned_in_block(scopes, &node.body);
2345 if let Some(else_block) = &node.else_block {
2346 forget_assigned_in_block(scopes, else_block);
2347 }
2348 }
2349 Statement::SwitchStatement(node) => {
2350 propagate_constants_expression(&mut node.discriminant, scopes, false);
2351 let mut case_assigned_scopes = scopes.clone();
2352 for case in &node.cases {
2353 forget_assigned_in_statements(&mut case_assigned_scopes, &case.consequent);
2354 }
2355 if let Some(default) = &node.default {
2356 forget_assigned_in_statements(&mut case_assigned_scopes, default);
2357 }
2358 for case in &mut node.cases {
2359 for value in &mut case.values {
2360 propagate_constants_expression(value, scopes, false);
2361 }
2362 let mut case_scopes = case_assigned_scopes.clone();
2363 case_scopes.push(HashMap::new());
2364 propagate_constants_statements(&mut case.consequent, &mut case_scopes);
2365 }
2366 if let Some(default) = &mut node.default {
2367 let mut default_scopes = case_assigned_scopes.clone();
2368 default_scopes.push(HashMap::new());
2369 propagate_constants_statements(default, &mut default_scopes);
2370 }
2371 for case in &node.cases {
2372 forget_assigned_in_statements(scopes, &case.consequent);
2373 }
2374 if let Some(default) = &node.default {
2375 forget_assigned_in_statements(scopes, default);
2376 }
2377 }
2378 Statement::TryStatement(node) => {
2379 let mut body_scopes = scopes.clone();
2380 propagate_constants_block(&mut node.body, &mut body_scopes);
2381 for handler in &mut node.handlers {
2382 let mut handler_scopes = scopes.clone();
2383 handler_scopes.push(HashMap::new());
2384 if let Some(name) = handler
2385 .binding
2386 .as_ref()
2387 .and_then(|binding| binding.name.as_ref())
2388 {
2389 forget_constant(&mut handler_scopes, name);
2390 }
2391 propagate_constants_statements(&mut handler.body.statements, &mut handler_scopes);
2392 }
2393 forget_assigned_in_block(scopes, &node.body);
2394 for handler in &node.handlers {
2395 forget_assigned_in_block(scopes, &handler.body);
2396 }
2397 }
2398 Statement::ReturnStatement(node) => {
2399 if let Some(argument) = &mut node.argument {
2400 propagate_constants_expression(argument, scopes, false);
2401 }
2402 }
2403 Statement::ThrowStatement(node) => {
2404 propagate_constants_expression(&mut node.argument, scopes, false);
2405 }
2406 Statement::DieStatement(node) => {
2407 propagate_constants_expression(&mut node.argument, scopes, false);
2408 }
2409 Statement::PostfixConditionalStatement(node) => {
2410 propagate_constants_statement(&mut node.statement, scopes);
2411 propagate_constants_expression(&mut node.test, scopes, false);
2412 }
2413 Statement::KeywordStatement(node) => {
2414 for argument in &mut node.arguments {
2415 propagate_constants_expression(argument, scopes, false);
2416 }
2417 }
2418 Statement::ExpressionStatement(node) => {
2419 propagate_constants_expression(&mut node.expression, scopes, false);
2420 }
2421 Statement::LoopControlStatement(_) => {}
2422 }
2423}
2424
2425fn propagate_constants_block(
2426 block: &mut BlockStatement,
2427 scopes: &mut Vec<HashMap<String, Expression>>,
2428) {
2429 if block.needs_lexical_scope {
2430 scopes.push(HashMap::new());
2431 propagate_constants_statements(&mut block.statements, scopes);
2432 scopes.pop();
2433 } else {
2434 propagate_constants_statements(&mut block.statements, scopes);
2435 }
2436}
2437
2438fn propagate_constants_class_member(member: &mut ClassMember) {
2439 match member {
2440 ClassMember::Field(field) => {
2441 if let Some(default_value) = &mut field.default_value {
2442 let mut scopes = vec![HashMap::new()];
2443 propagate_constants_expression(default_value, &mut scopes, false);
2444 }
2445 }
2446 ClassMember::Method(method) => {
2447 let _ = method;
2448 }
2449 ClassMember::Class(class) => {
2450 for member in &mut class.body {
2451 propagate_constants_class_member(member);
2452 }
2453 }
2454 ClassMember::Trait(trait_decl) => {
2455 for member in &mut trait_decl.body {
2456 propagate_constants_class_member(member);
2457 }
2458 }
2459 }
2460}
2461
2462fn record_constant_declaration(
2463 statement: &Statement,
2464 scopes: &mut Vec<HashMap<String, Expression>>,
2465) {
2466 match statement {
2467 Statement::VariableDeclaration(node) => {
2468 if let Some(init) = node.init.as_ref().and_then(literal_constant) {
2469 if let Some(scope) = scopes.last_mut() {
2470 scope.insert(node.name.clone(), init);
2471 }
2472 } else {
2473 forget_constant(scopes, &node.name);
2474 }
2475 }
2476 Statement::VariableUnpackDeclaration(node) => {
2477 for entry in node.pattern.entries() {
2478 forget_constant(scopes, &entry.name);
2479 }
2480 }
2481 Statement::FunctionDeclaration(node) => forget_constant(scopes, &node.name),
2482 Statement::ClassDeclaration(node) => forget_constant(scopes, &node.name),
2483 Statement::TraitDeclaration(node) => forget_constant(scopes, &node.name),
2484 Statement::ImportDeclaration(node) => {
2485 for specifier in &node.specifiers {
2486 forget_constant(scopes, &specifier.local);
2487 }
2488 }
2489 _ => {}
2490 }
2491}
2492
2493fn propagate_constants_expression(
2494 expr: &mut Expression,
2495 scopes: &mut Vec<HashMap<String, Expression>>,
2496 lvalue_context: bool,
2497) {
2498 match expr {
2499 Expression::Identifier {
2500 line,
2501 source_file,
2502 name,
2503 inferred_type,
2504 ..
2505 } if !lvalue_context => {
2506 if let Some(value) = lookup_constant(scopes, name) {
2507 *expr = constant_for_use(&value, *line, source_file.clone(), inferred_type.clone());
2508 }
2509 }
2510 Expression::Unary {
2511 operator, argument, ..
2512 } if operator == "\\" => {
2513 propagate_constants_expression(argument, scopes, true);
2514 }
2515 Expression::Unary {
2516 operator, argument, ..
2517 } if matches!(operator.as_str(), "++" | "--") => {
2518 propagate_constants_expression(argument, scopes, true);
2519 forget_lvalue_constants(scopes, argument);
2520 }
2521 Expression::Unary { argument, .. } => {
2522 propagate_constants_expression(argument, scopes, false);
2523 }
2524 Expression::Binary { left, right, .. } | Expression::DefinedOr { left, right, .. } => {
2525 propagate_constants_expression(left, scopes, false);
2526 propagate_constants_expression(right, scopes, false);
2527 }
2528 Expression::Assignment {
2529 operator,
2530 left,
2531 right,
2532 ..
2533 } => {
2534 propagate_constants_expression(left, scopes, true);
2535 if operator == "~=" {
2536 propagate_regex_replacement_pattern(right, scopes);
2537 } else {
2538 propagate_constants_expression(right, scopes, false);
2539 }
2540 forget_lvalue_constants(scopes, left);
2541 }
2542 Expression::Ternary {
2543 test,
2544 consequent,
2545 alternate,
2546 ..
2547 } => {
2548 propagate_constants_expression(test, scopes, false);
2549 let mut consequent_scopes = scopes.clone();
2550 propagate_constants_expression(consequent, &mut consequent_scopes, false);
2551 let mut alternate_scopes = scopes.clone();
2552 propagate_constants_expression(alternate, &mut alternate_scopes, false);
2553 }
2554 Expression::Call {
2555 callee, arguments, ..
2556 } => {
2557 let mutates_caller_scope = call_may_mutate_caller_scope(callee);
2558 propagate_constants_expression(callee, scopes, false);
2559 for argument in arguments {
2560 propagate_constants_call_argument(argument, scopes);
2561 }
2562 if mutates_caller_scope {
2563 clear_constants(scopes);
2564 }
2565 }
2566 Expression::MemberAccess { object, .. } => {
2567 propagate_constants_expression(object, scopes, lvalue_context);
2568 }
2569 Expression::DynamicMemberCall {
2570 object,
2571 member,
2572 arguments,
2573 ..
2574 } => {
2575 propagate_constants_expression(object, scopes, false);
2576 propagate_constants_expression(member, scopes, false);
2577 for argument in arguments {
2578 propagate_constants_call_argument(argument, scopes);
2579 }
2580 }
2581 Expression::Index { object, index, .. } => {
2582 propagate_constants_expression(object, scopes, lvalue_context);
2583 propagate_constants_expression(index, scopes, false);
2584 }
2585 Expression::Slice {
2586 object, start, end, ..
2587 } => {
2588 propagate_constants_expression(object, scopes, lvalue_context);
2589 if let Some(start) = start {
2590 propagate_constants_expression(start, scopes, false);
2591 }
2592 if let Some(end) = end {
2593 propagate_constants_expression(end, scopes, false);
2594 }
2595 }
2596 Expression::DictAccess { object, key, .. } => {
2597 propagate_constants_expression(object, scopes, lvalue_context);
2598 propagate_constants_dict_key(key, scopes);
2599 }
2600 Expression::PostfixUpdate { argument, .. } => {
2601 propagate_constants_expression(argument, scopes, true);
2602 forget_lvalue_constants(scopes, argument);
2603 }
2604 Expression::Lambda { body, .. } => {
2605 forget_assigned_in_expression(scopes, body);
2606 }
2607 Expression::FunctionExpression { body, .. } => {
2608 forget_assigned_in_block(scopes, body);
2609 }
2610 Expression::LetExpression { name, init, .. } => {
2611 if let Some(init) = init {
2612 propagate_constants_expression(init, scopes, false);
2613 if let Some(value) = literal_constant(init) {
2614 if let Some(scope) = scopes.last_mut() {
2615 scope.insert(name.clone(), value);
2616 }
2617 } else {
2618 forget_constant(scopes, name);
2619 }
2620 } else {
2621 forget_constant(scopes, name);
2622 }
2623 }
2624 Expression::TryExpression { body, handlers, .. } => {
2625 let mut body_scopes = scopes.clone();
2626 propagate_constants_block(body, &mut body_scopes);
2627 for handler in handlers {
2628 let mut handler_scopes = scopes.clone();
2629 handler_scopes.push(HashMap::new());
2630 if let Some(name) = handler
2631 .binding
2632 .as_ref()
2633 .and_then(|binding| binding.name.as_ref())
2634 {
2635 forget_constant(&mut handler_scopes, name);
2636 }
2637 propagate_constants_statements(&mut handler.body.statements, &mut handler_scopes);
2638 }
2639 }
2640 Expression::DoExpression { body, .. }
2641 | Expression::AwaitExpression { body, .. }
2642 | Expression::SpawnExpression { body, .. } => {
2643 let mut body_scopes = scopes.clone();
2644 propagate_constants_block(body, &mut body_scopes);
2645 forget_assigned_in_block(scopes, body);
2646 }
2647 Expression::ArrayLiteral { elements, .. }
2648 | Expression::SetLiteral { elements, .. }
2649 | Expression::BagLiteral { elements, .. } => {
2650 for element in elements {
2651 propagate_constants_expression(element, scopes, false);
2652 }
2653 }
2654 Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
2655 for entry in entries {
2656 propagate_constants_dict_key(&mut entry.key, scopes);
2657 propagate_constants_expression(&mut entry.value, scopes, false);
2658 }
2659 }
2660 Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
2661 for part in parts {
2662 if let TemplatePart::Expression { expression, .. } = part {
2663 propagate_constants_expression(expression, scopes, false);
2664 }
2665 }
2666 }
2667 Expression::SuperCall { arguments, .. } => {
2668 for argument in arguments {
2669 propagate_constants_call_argument(argument, scopes);
2670 }
2671 }
2672 Expression::Identifier { .. }
2673 | Expression::NumberLiteral { .. }
2674 | Expression::StringLiteral { .. }
2675 | Expression::BinaryStringLiteral { .. }
2676 | Expression::BooleanLiteral { .. }
2677 | Expression::NullLiteral { .. } => {}
2678 }
2679 if !lvalue_context {
2680 if let Some(folded) = fold_expression(expr) {
2681 *expr = folded;
2682 }
2683 }
2684}
2685
2686fn propagate_constants_call_argument(
2687 argument: &mut CallArgument,
2688 scopes: &mut Vec<HashMap<String, Expression>>,
2689) {
2690 match argument {
2691 CallArgument::Positional { value, .. } => {
2692 propagate_constants_expression(value, scopes, false);
2693 }
2694 CallArgument::Spread { value, .. } => {
2695 propagate_constants_expression(value, scopes, false);
2696 }
2697 CallArgument::Named { name, value, .. } => {
2698 propagate_constants_dict_key(name, scopes);
2699 propagate_constants_expression(value, scopes, false);
2700 }
2701 }
2702}
2703
2704fn call_may_mutate_caller_scope(callee: &Expression) -> bool {
2705 matches!(callee, Expression::Identifier { name, .. } if name == "eval")
2706}
2707
2708fn clear_constants(scopes: &mut [HashMap<String, Expression>]) {
2709 for scope in scopes {
2710 scope.clear();
2711 }
2712}
2713
2714fn propagate_regex_replacement_pattern(
2715 replacement: &mut Expression,
2716 scopes: &mut Vec<HashMap<String, Expression>>,
2717) {
2718 if let Expression::Binary { operator, left, .. } = replacement {
2719 if operator == "->" {
2720 propagate_constants_expression(left, scopes, false);
2721 }
2722 }
2723}
2724
2725fn propagate_constants_dict_key(key: &mut DictKey, scopes: &mut Vec<HashMap<String, Expression>>) {
2726 if let DictKey::Expression { expression, .. } = key {
2727 propagate_constants_expression(expression, scopes, false);
2728 }
2729}
2730
2731fn lookup_constant(scopes: &[HashMap<String, Expression>], name: &str) -> Option<Expression> {
2732 for scope in scopes.iter().rev() {
2733 if let Some(value) = scope.get(name) {
2734 return Some(value.clone());
2735 }
2736 }
2737 None
2738}
2739
2740fn forget_constant(scopes: &mut [HashMap<String, Expression>], name: &str) {
2741 for scope in scopes.iter_mut().rev() {
2742 if scope.remove(name).is_some() {
2743 return;
2744 }
2745 }
2746}
2747
2748fn forget_lvalue_constants(scopes: &mut [HashMap<String, Expression>], target: &Expression) {
2749 match target {
2750 Expression::Identifier { name, .. } => forget_constant(scopes, name),
2751 Expression::Binary { left, .. } => forget_lvalue_constants(scopes, left),
2752 Expression::MemberAccess { object, .. }
2753 | Expression::Index { object, .. }
2754 | Expression::Slice { object, .. }
2755 | Expression::DictAccess { object, .. } => forget_lvalue_constants(scopes, object),
2756 _ => {}
2757 }
2758}
2759
2760fn forget_assigned_in_block(scopes: &mut [HashMap<String, Expression>], block: &BlockStatement) {
2761 forget_assigned_in_statements(scopes, &block.statements);
2762}
2763
2764fn forget_assigned_in_statements(
2765 scopes: &mut [HashMap<String, Expression>],
2766 statements: &[Statement],
2767) {
2768 for statement in statements {
2769 forget_assigned_in_statement(scopes, statement);
2770 }
2771}
2772
2773fn forget_assigned_in_statement(scopes: &mut [HashMap<String, Expression>], statement: &Statement) {
2774 match statement {
2775 Statement::Block(block) => forget_assigned_in_block(scopes, block),
2776 Statement::VariableDeclaration(_) => {}
2777 Statement::VariableUnpackDeclaration(_) => {}
2778 Statement::FunctionDeclaration(_) => {}
2779 Statement::ClassDeclaration(_) => {}
2780 Statement::TraitDeclaration(_) => {}
2781 Statement::ImportDeclaration(_) => {}
2782 Statement::IfStatement(node) => {
2783 forget_assigned_in_block(scopes, &node.consequent);
2784 if let Some(alternate) = &node.alternate {
2785 forget_assigned_in_statement(scopes, alternate);
2786 }
2787 }
2788 Statement::WhileStatement(node) => forget_assigned_in_block(scopes, &node.body),
2789 Statement::ForStatement(node) => {
2790 forget_assigned_in_block(scopes, &node.body);
2791 if let Some(else_block) = &node.else_block {
2792 forget_assigned_in_block(scopes, else_block);
2793 }
2794 }
2795 Statement::SwitchStatement(node) => {
2796 for case in &node.cases {
2797 forget_assigned_in_statements(scopes, &case.consequent);
2798 }
2799 if let Some(default) = &node.default {
2800 forget_assigned_in_statements(scopes, default);
2801 }
2802 }
2803 Statement::TryStatement(node) => {
2804 forget_assigned_in_block(scopes, &node.body);
2805 for handler in &node.handlers {
2806 forget_assigned_in_block(scopes, &handler.body);
2807 }
2808 }
2809 Statement::ReturnStatement(node) => {
2810 if let Some(argument) = &node.argument {
2811 forget_assigned_in_expression(scopes, argument);
2812 }
2813 }
2814 Statement::ThrowStatement(node) => forget_assigned_in_expression(scopes, &node.argument),
2815 Statement::DieStatement(node) => forget_assigned_in_expression(scopes, &node.argument),
2816 Statement::PostfixConditionalStatement(node) => {
2817 forget_assigned_in_statement(scopes, &node.statement);
2818 forget_assigned_in_expression(scopes, &node.test);
2819 }
2820 Statement::KeywordStatement(node) => {
2821 for argument in &node.arguments {
2822 forget_assigned_in_expression(scopes, argument);
2823 }
2824 }
2825 Statement::ExpressionStatement(node) => {
2826 forget_assigned_in_expression(scopes, &node.expression);
2827 }
2828 Statement::LoopControlStatement(_) => {}
2829 }
2830}
2831
2832fn forget_assigned_in_expression(scopes: &mut [HashMap<String, Expression>], expr: &Expression) {
2833 match expr {
2834 Expression::Unary {
2835 operator, argument, ..
2836 } if matches!(operator.as_str(), "++" | "--") => {
2837 forget_lvalue_constants(scopes, argument);
2838 }
2839 Expression::Unary { argument, .. } => forget_assigned_in_expression(scopes, argument),
2840 Expression::Binary { left, right, .. } | Expression::DefinedOr { left, right, .. } => {
2841 forget_assigned_in_expression(scopes, left);
2842 forget_assigned_in_expression(scopes, right);
2843 }
2844 Expression::Assignment { left, right, .. } => {
2845 forget_lvalue_constants(scopes, left);
2846 forget_assigned_in_expression(scopes, right);
2847 }
2848 Expression::Ternary {
2849 test,
2850 consequent,
2851 alternate,
2852 ..
2853 } => {
2854 forget_assigned_in_expression(scopes, test);
2855 forget_assigned_in_expression(scopes, consequent);
2856 forget_assigned_in_expression(scopes, alternate);
2857 }
2858 Expression::Call {
2859 callee, arguments, ..
2860 } => {
2861 forget_assigned_in_expression(scopes, callee);
2862 for argument in arguments {
2863 forget_assigned_in_call_argument(scopes, argument);
2864 }
2865 }
2866 Expression::MemberAccess { object, .. } => forget_assigned_in_expression(scopes, object),
2867 Expression::DynamicMemberCall {
2868 object,
2869 member,
2870 arguments,
2871 ..
2872 } => {
2873 forget_assigned_in_expression(scopes, object);
2874 forget_assigned_in_expression(scopes, member);
2875 for argument in arguments {
2876 forget_assigned_in_call_argument(scopes, argument);
2877 }
2878 }
2879 Expression::Index { object, index, .. } => {
2880 forget_assigned_in_expression(scopes, object);
2881 forget_assigned_in_expression(scopes, index);
2882 }
2883 Expression::Slice {
2884 object, start, end, ..
2885 } => {
2886 forget_assigned_in_expression(scopes, object);
2887 if let Some(start) = start {
2888 forget_assigned_in_expression(scopes, start);
2889 }
2890 if let Some(end) = end {
2891 forget_assigned_in_expression(scopes, end);
2892 }
2893 }
2894 Expression::DictAccess { object, key, .. } => {
2895 forget_assigned_in_expression(scopes, object);
2896 forget_assigned_in_dict_key(scopes, key);
2897 }
2898 Expression::PostfixUpdate { argument, .. } => forget_lvalue_constants(scopes, argument),
2899 Expression::LetExpression { init, .. } => {
2900 if let Some(init) = init {
2901 forget_assigned_in_expression(scopes, init);
2902 }
2903 }
2904 Expression::TryExpression { body, handlers, .. } => {
2905 forget_assigned_in_block(scopes, body);
2906 for handler in handlers {
2907 forget_assigned_in_block(scopes, &handler.body);
2908 }
2909 }
2910 Expression::DoExpression { body, .. }
2911 | Expression::AwaitExpression { body, .. }
2912 | Expression::SpawnExpression { body, .. } => forget_assigned_in_block(scopes, body),
2913 Expression::ArrayLiteral { elements, .. }
2914 | Expression::SetLiteral { elements, .. }
2915 | Expression::BagLiteral { elements, .. } => {
2916 for element in elements {
2917 forget_assigned_in_expression(scopes, element);
2918 }
2919 }
2920 Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
2921 for entry in entries {
2922 forget_assigned_in_dict_key(scopes, &entry.key);
2923 forget_assigned_in_expression(scopes, &entry.value);
2924 }
2925 }
2926 Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
2927 for part in parts {
2928 if let TemplatePart::Expression { expression, .. } = part {
2929 forget_assigned_in_expression(scopes, expression);
2930 }
2931 }
2932 }
2933 Expression::SuperCall { arguments, .. } => {
2934 for argument in arguments {
2935 forget_assigned_in_call_argument(scopes, argument);
2936 }
2937 }
2938 Expression::Lambda { .. }
2939 | Expression::FunctionExpression { .. }
2940 | Expression::Identifier { .. }
2941 | Expression::NumberLiteral { .. }
2942 | Expression::StringLiteral { .. }
2943 | Expression::BinaryStringLiteral { .. }
2944 | Expression::BooleanLiteral { .. }
2945 | Expression::NullLiteral { .. } => {}
2946 }
2947}
2948
2949fn forget_assigned_in_call_argument(
2950 scopes: &mut [HashMap<String, Expression>],
2951 argument: &CallArgument,
2952) {
2953 match argument {
2954 CallArgument::Positional { value, .. } => forget_assigned_in_expression(scopes, value),
2955 CallArgument::Spread { value, .. } => forget_assigned_in_expression(scopes, value),
2956 CallArgument::Named { name, value, .. } => {
2957 forget_assigned_in_dict_key(scopes, name);
2958 forget_assigned_in_expression(scopes, value);
2959 }
2960 }
2961}
2962
2963fn forget_assigned_in_dict_key(scopes: &mut [HashMap<String, Expression>], key: &DictKey) {
2964 if let DictKey::Expression { expression, .. } = key {
2965 forget_assigned_in_expression(scopes, expression);
2966 }
2967}
2968
2969fn literal_constant(expr: &Expression) -> Option<Expression> {
2970 match expr {
2971 Expression::NumberLiteral { .. }
2972 | Expression::StringLiteral { .. }
2973 | Expression::BooleanLiteral { .. }
2974 | Expression::NullLiteral { .. } => Some(expr.clone()),
2975 _ => None,
2976 }
2977}
2978
2979fn constant_for_use(
2980 value: &Expression,
2981 line: usize,
2982 source_file: Option<String>,
2983 inferred_type: Option<String>,
2984) -> Expression {
2985 match value {
2986 Expression::NumberLiteral { value, .. } => Expression::NumberLiteral {
2987 line,
2988 source_file,
2989 value: value.clone(),
2990 inferred_type,
2991 },
2992 Expression::StringLiteral { value, .. } => Expression::StringLiteral {
2993 line,
2994 source_file,
2995 value: value.clone(),
2996 inferred_type,
2997 },
2998 Expression::BooleanLiteral { value, .. } => Expression::BooleanLiteral {
2999 line,
3000 source_file,
3001 value: *value,
3002 inferred_type,
3003 },
3004 Expression::NullLiteral { .. } => Expression::NullLiteral {
3005 line,
3006 source_file,
3007 inferred_type,
3008 },
3009 other => other.clone(),
3010 }
3011}
3012
3013fn annotate_identifier_depths(program: &mut Program) {
3014 let mut scopes = vec![root_scope()];
3015 annotate_statements(&mut program.statements, &mut scopes);
3016}
3017
3018fn root_scope() -> HashSet<String> {
3019 [
3020 "Exception",
3021 "AssertionException",
3022 "TypeException",
3023 "CancelledException",
3024 "TimeoutException",
3025 "ChannelClosedException",
3026 "Array",
3027 "Dict",
3028 "PairList",
3029 "Set",
3030 "Bag",
3031 "Pair",
3032 "String",
3033 "BinaryString",
3034 "Number",
3035 "Boolean",
3036 "Regexp",
3037 "Function",
3038 "__file__",
3039 "__global__",
3040 "__system__",
3041 ]
3042 .into_iter()
3043 .map(str::to_owned)
3044 .collect()
3045}
3046
3047fn annotate_statements(statements: &mut [Statement], scopes: &mut Vec<HashSet<String>>) {
3048 for statement in statements {
3049 annotate_statement(statement, scopes);
3050 declare_statement_binding(statement, scopes);
3051 }
3052}
3053
3054fn annotate_statement(statement: &mut Statement, scopes: &mut Vec<HashSet<String>>) {
3055 match statement {
3056 Statement::Block(block) => {
3057 annotate_block(block, scopes);
3058 }
3059 Statement::VariableDeclaration(node) => {
3060 if let Some(init) = &mut node.init {
3061 annotate_expression(init, scopes);
3062 }
3063 }
3064 Statement::VariableUnpackDeclaration(node) => {
3065 annotate_expression(&mut node.init, scopes);
3066 for entry in node.pattern.entries_mut() {
3067 annotate_dict_key(&mut entry.key, scopes);
3068 if let Some(default_value) = &mut entry.default_value {
3069 annotate_expression(default_value, scopes);
3070 }
3071 }
3072 }
3073 Statement::FunctionDeclaration(node) => {
3074 scopes.push(function_scope(&node.params));
3075 annotate_statements(&mut node.body.statements, scopes);
3076 scopes.pop();
3077 }
3078 Statement::ClassDeclaration(node) => {
3079 for member in &mut node.body {
3080 annotate_class_member(member, scopes);
3081 }
3082 }
3083 Statement::TraitDeclaration(node) => {
3084 for member in &mut node.body {
3085 annotate_class_member(member, scopes);
3086 }
3087 }
3088 Statement::ImportDeclaration(node) => {
3089 if let Some(condition) = &mut node.condition {
3090 annotate_expression(&mut condition.test, scopes);
3091 }
3092 }
3093 Statement::IfStatement(node) => {
3094 annotate_expression(&mut node.test, scopes);
3095 annotate_block(&mut node.consequent, scopes);
3096 if let Some(alternate) = &mut node.alternate {
3097 annotate_statement(alternate, scopes);
3098 }
3099 }
3100 Statement::WhileStatement(node) => {
3101 annotate_expression(&mut node.test, scopes);
3102 annotate_block(&mut node.body, scopes);
3103 }
3104 Statement::ForStatement(node) => {
3105 annotate_expression(&mut node.iterable, scopes);
3106 let mut scope = HashSet::new();
3107 scope.insert(node.variable.clone());
3108 scopes.push(scope);
3109 annotate_statements(&mut node.body.statements, scopes);
3110 scopes.pop();
3111 if let Some(else_block) = &mut node.else_block {
3112 annotate_block(else_block, scopes);
3113 }
3114 }
3115 Statement::SwitchStatement(node) => {
3116 annotate_expression(&mut node.discriminant, scopes);
3117 for case in &mut node.cases {
3118 for value in &mut case.values {
3119 annotate_expression(value, scopes);
3120 }
3121 scopes.push(HashSet::new());
3122 annotate_statements(&mut case.consequent, scopes);
3123 scopes.pop();
3124 }
3125 if let Some(default) = &mut node.default {
3126 scopes.push(HashSet::new());
3127 annotate_statements(default, scopes);
3128 scopes.pop();
3129 }
3130 }
3131 Statement::TryStatement(node) => {
3132 annotate_block(&mut node.body, scopes);
3133 for handler in &mut node.handlers {
3134 let mut scope = HashSet::new();
3135 if let Some(name) = handler
3136 .binding
3137 .as_ref()
3138 .and_then(|binding| binding.name.clone())
3139 {
3140 scope.insert(name);
3141 }
3142 scopes.push(scope);
3143 annotate_statements(&mut handler.body.statements, scopes);
3144 scopes.pop();
3145 }
3146 }
3147 Statement::ReturnStatement(node) => {
3148 if let Some(argument) = &mut node.argument {
3149 annotate_expression(argument, scopes);
3150 }
3151 }
3152 Statement::ThrowStatement(node) => annotate_expression(&mut node.argument, scopes),
3153 Statement::DieStatement(node) => annotate_expression(&mut node.argument, scopes),
3154 Statement::PostfixConditionalStatement(node) => {
3155 annotate_statement(&mut node.statement, scopes);
3156 annotate_expression(&mut node.test, scopes);
3157 }
3158 Statement::KeywordStatement(node) => {
3159 for argument in &mut node.arguments {
3160 annotate_expression(argument, scopes);
3161 }
3162 }
3163 Statement::ExpressionStatement(node) => annotate_expression(&mut node.expression, scopes),
3164 Statement::LoopControlStatement(_) => {}
3165 }
3166}
3167
3168fn declare_statement_binding(statement: &Statement, scopes: &mut [HashSet<String>]) {
3169 let Some(scope) = scopes.last_mut() else {
3170 return;
3171 };
3172 match statement {
3173 Statement::VariableDeclaration(node) => {
3174 scope.insert(node.name.clone());
3175 }
3176 Statement::VariableUnpackDeclaration(node) => {
3177 for entry in node.pattern.entries() {
3178 scope.insert(entry.name.clone());
3179 }
3180 }
3181 Statement::FunctionDeclaration(node) => {
3182 scope.insert(node.name.clone());
3183 }
3184 Statement::ClassDeclaration(node) => {
3185 scope.insert(node.name.clone());
3186 }
3187 Statement::TraitDeclaration(node) => {
3188 scope.insert(node.name.clone());
3189 }
3190 Statement::ImportDeclaration(node) => {
3191 for specifier in &node.specifiers {
3192 scope.insert(specifier.local.clone());
3193 }
3194 }
3195 _ => {}
3196 }
3197}
3198
3199fn annotate_block(block: &mut BlockStatement, scopes: &mut Vec<HashSet<String>>) {
3200 if block.needs_lexical_scope {
3201 scopes.push(HashSet::new());
3202 annotate_statements(&mut block.statements, scopes);
3203 scopes.pop();
3204 } else {
3205 annotate_statements(&mut block.statements, scopes);
3206 }
3207}
3208
3209fn function_scope(params: &[crate::ast::Parameter]) -> HashSet<String> {
3210 params.iter().map(|param| param.name.clone()).collect()
3211}
3212
3213fn annotate_class_member(member: &mut ClassMember, scopes: &mut Vec<HashSet<String>>) {
3214 match member {
3215 ClassMember::Field(field) => {
3216 if let Some(default_value) = &mut field.default_value {
3217 annotate_expression(default_value, scopes);
3218 }
3219 }
3220 ClassMember::Method(method) => {
3221 let mut scope = function_scope(&method.params);
3222 scope.insert("self".to_owned());
3223 scopes.push(scope);
3224 annotate_statements(&mut method.body.statements, scopes);
3225 scopes.pop();
3226 }
3227 ClassMember::Class(class) => {
3228 for member in &mut class.body {
3229 annotate_class_member(member, scopes);
3230 }
3231 }
3232 ClassMember::Trait(trait_decl) => {
3233 for member in &mut trait_decl.body {
3234 annotate_class_member(member, scopes);
3235 }
3236 }
3237 }
3238}
3239
3240fn annotate_expression(expr: &mut Expression, scopes: &mut Vec<HashSet<String>>) {
3241 match expr {
3242 Expression::Identifier {
3243 name,
3244 binding_depth,
3245 ..
3246 } => {
3247 *binding_depth = resolve_depth(name, scopes);
3248 }
3249 Expression::Unary { argument, .. } => annotate_expression(argument, scopes),
3250 Expression::Binary { left, right, .. }
3251 | Expression::DefinedOr { left, right, .. }
3252 | Expression::Assignment { left, right, .. } => {
3253 annotate_expression(left, scopes);
3254 annotate_expression(right, scopes);
3255 }
3256 Expression::Ternary {
3257 test,
3258 consequent,
3259 alternate,
3260 ..
3261 } => {
3262 annotate_expression(test, scopes);
3263 annotate_expression(consequent, scopes);
3264 annotate_expression(alternate, scopes);
3265 }
3266 Expression::Call {
3267 callee, arguments, ..
3268 } => {
3269 annotate_expression(callee, scopes);
3270 for argument in arguments {
3271 annotate_call_argument(argument, scopes);
3272 }
3273 }
3274 Expression::MemberAccess { object, .. } => annotate_expression(object, scopes),
3275 Expression::DynamicMemberCall {
3276 object,
3277 member,
3278 arguments,
3279 ..
3280 } => {
3281 annotate_expression(object, scopes);
3282 annotate_expression(member, scopes);
3283 for argument in arguments {
3284 annotate_call_argument(argument, scopes);
3285 }
3286 }
3287 Expression::Index { object, index, .. } => {
3288 annotate_expression(object, scopes);
3289 annotate_expression(index, scopes);
3290 }
3291 Expression::Slice {
3292 object, start, end, ..
3293 } => {
3294 annotate_expression(object, scopes);
3295 if let Some(start) = start {
3296 annotate_expression(start, scopes);
3297 }
3298 if let Some(end) = end {
3299 annotate_expression(end, scopes);
3300 }
3301 }
3302 Expression::DictAccess { object, key, .. } => {
3303 annotate_expression(object, scopes);
3304 annotate_dict_key(key, scopes);
3305 }
3306 Expression::PostfixUpdate { argument, .. } => annotate_expression(argument, scopes),
3307 Expression::Lambda { params, body, .. } => {
3308 scopes.push(function_scope(params));
3309 annotate_expression(body, scopes);
3310 scopes.pop();
3311 }
3312 Expression::FunctionExpression { params, body, .. } => {
3313 scopes.push(function_scope(params));
3314 annotate_statements(&mut body.statements, scopes);
3315 scopes.pop();
3316 }
3317 Expression::LetExpression { name, init, .. } => {
3318 if let Some(init) = init {
3319 annotate_expression(init, scopes);
3320 }
3321 if let Some(scope) = scopes.last_mut() {
3322 scope.insert(name.clone());
3323 }
3324 }
3325 Expression::TryExpression { body, handlers, .. } => {
3326 annotate_block(body, scopes);
3327 for handler in handlers {
3328 let mut scope = HashSet::new();
3329 if let Some(name) = handler
3330 .binding
3331 .as_ref()
3332 .and_then(|binding| binding.name.clone())
3333 {
3334 scope.insert(name);
3335 }
3336 scopes.push(scope);
3337 annotate_statements(&mut handler.body.statements, scopes);
3338 scopes.pop();
3339 }
3340 }
3341 Expression::DoExpression { body, .. }
3342 | Expression::AwaitExpression { body, .. }
3343 | Expression::SpawnExpression { body, .. } => {
3344 annotate_block(body, scopes);
3345 }
3346 Expression::ArrayLiteral { elements, .. }
3347 | Expression::SetLiteral { elements, .. }
3348 | Expression::BagLiteral { elements, .. } => {
3349 for element in elements {
3350 annotate_expression(element, scopes);
3351 }
3352 }
3353 Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
3354 for entry in entries {
3355 annotate_dict_key(&mut entry.key, scopes);
3356 annotate_expression(&mut entry.value, scopes);
3357 }
3358 }
3359 Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
3360 for part in parts {
3361 if let TemplatePart::Expression { expression, .. } = part {
3362 annotate_expression(expression, scopes);
3363 }
3364 }
3365 }
3366 Expression::SuperCall { arguments, .. } => {
3367 for argument in arguments {
3368 annotate_call_argument(argument, scopes);
3369 }
3370 }
3371 Expression::NumberLiteral { .. }
3372 | Expression::StringLiteral { .. }
3373 | Expression::BinaryStringLiteral { .. }
3374 | Expression::BooleanLiteral { .. }
3375 | Expression::NullLiteral { .. } => {}
3376 }
3377}
3378
3379fn annotate_call_argument(argument: &mut CallArgument, scopes: &mut Vec<HashSet<String>>) {
3380 match argument {
3381 CallArgument::Positional { value, .. } => annotate_expression(value, scopes),
3382 CallArgument::Spread { value, .. } => annotate_expression(value, scopes),
3383 CallArgument::Named { name, value, .. } => {
3384 annotate_dict_key(name, scopes);
3385 annotate_expression(value, scopes);
3386 }
3387 }
3388}
3389
3390fn annotate_dict_key(key: &mut DictKey, scopes: &mut Vec<HashSet<String>>) {
3391 if let DictKey::Expression { expression, .. } = key {
3392 annotate_expression(expression, scopes);
3393 }
3394}
3395
3396fn resolve_depth(name: &str, scopes: &[HashSet<String>]) -> Option<usize> {
3397 for (depth, scope) in scopes.iter().rev().enumerate() {
3398 if scope.contains(name) {
3399 return Some(depth);
3400 }
3401 }
3402 None
3403}