···33 expression::{self, Generator, Ordering, float, int},
44};
55use crate::{
66- ast::{AssignmentKind, Endianness, SrcSpan, TypedClause, TypedExpr},
66+ ast::{AssignmentKind, Endianness, SrcSpan, TypedClause, TypedExpr, TypedPattern},
77 docvec,
88 exhaustiveness::{
99 BitArrayMatchedValue, BitArrayTest, Body, BoundValue, CompiledCase, Decision,
···1515 expression::{eco_string_int, string},
1616 maybe_escape_property,
1717 },
1818- pretty::{Document, Documentable, break_, join, line, nil},
1818+ pretty::{Document, Documentable, break_, concat, join, line, nil},
1919 strings::{
2020 convert_string_escape_chars, length_utf16, string_to_utf16_bytes, string_to_utf32_bytes,
2121 },
···2323use ecow::{EcoString, eco_format};
2424use itertools::Itertools;
2525use num_bigint::BigInt;
2626-use std::{
2727- collections::{HashMap, VecDeque},
2828- sync::OnceLock,
2929-};
2626+use std::{collections::HashMap, sync::OnceLock};
30273128pub static ASSIGNMENT_VAR: &str = "$";
3229···3633 subjects: &'a [TypedExpr],
3734 expression_generator: &mut Generator<'_, 'a>,
3835) -> Document<'a> {
3939- let mut variables = Variables::new(expression_generator);
3636+ let mut variables = Variables::new(expression_generator, false);
4037 let assignments = variables.assign_case_subjects(compiled_case, subjects);
4141- let decision = CasePrinter { clauses, variables }.decision(&compiled_case.tree);
3838+ let decision = CasePrinter {
3939+ variables,
4040+ kind: DecisionKind::Case { clauses },
4141+ }
4242+ .decision(&compiled_case.tree);
4243 docvec![assignments_to_doc(assignments), decision.into_doc()].force_break()
4344}
4445···128129}
129130130131struct CasePrinter<'module, 'generator, 'a> {
131131- clauses: &'a [TypedClause],
132132 variables: Variables<'generator, 'module, 'a>,
133133+ kind: DecisionKind<'a>,
134134+}
135135+136136+enum DecisionKind<'a> {
137137+ Case {
138138+ clauses: &'a [TypedClause],
139139+ },
140140+ LetAssert {
141141+ kind: &'a AssignmentKind<TypedExpr>,
142142+ subject_location: SrcSpan,
143143+ pattern_location: SrcSpan,
144144+ subject: EcoString,
145145+ },
133146}
134147135148/// Code generation for decision trees can look a bit daunting at a first glance
···196209impl<'a> CasePrinter<'_, '_, 'a> {
197210 fn decision(&mut self, decision: &'a Decision) -> CaseBody<'a> {
198211 match decision {
199199- Decision::Fail => unreachable!("Invalid decision tree reached code generation"),
212212+ Decision::Fail => {
213213+ if let DecisionKind::LetAssert {
214214+ kind,
215215+ subject_location,
216216+ pattern_location,
217217+ subject,
218218+ } = &self.kind
219219+ {
220220+ CaseBody::Statements(self.assignment_no_match(
221221+ subject.to_doc(),
222222+ kind,
223223+ *subject_location,
224224+ *pattern_location,
225225+ ))
226226+ } else {
227227+ unreachable!("Invalid decision tree reached code generation")
228228+ }
229229+ }
200230 Decision::Run { body } => {
201231 let bindings = self.variables.bindings_doc(&body.bindings);
202232 let body = self.body_expression(body.clause_index);
···217247 }
218248219249 fn body_expression(&mut self, clause_index: usize) -> Document<'a> {
220220- let body = &self
221221- .clauses
250250+ let DecisionKind::Case { clauses } = &self.kind else {
251251+ return nil();
252252+ };
253253+254254+ let body = &clauses
222255 .get(clause_index)
223256 .expect("invalid clause index")
224257 .then;
···272305 // referenced by this check
273306 let (check_doc, body, mut segment_assignments) = self.inside_new_scope(|this| {
274307 let segment_assignments = this.variables.bit_array_segment_assignments(check);
275275- let check_doc = this
276276- .variables
277277- .runtime_check(var, check, CheckNegation::NotNegated);
308308+ let check_doc = this.variables.runtime_check(var, check);
278309 let body = this.decision(decision);
279310 (check_doc, body, segment_assignments)
280311 });
···447478 if_true: &'a Body,
448479 if_false: &'a Decision,
449480 ) -> CaseBody<'a> {
450450- let guard = self
451451- .clauses
481481+ let DecisionKind::Case { clauses } = &self.kind else {
482482+ unreachable!("Guards cannot appear in let assert decision trees")
483483+ };
484484+485485+ let guard = clauses
452486 .get(guard)
453487 .expect("invalid clause index")
454488 .guard
···496530 CaseBody::Statements(join_with_line(check_bindings, if_.into_doc()))
497531 }
498532 }
499499-}
500533501501-/// Prints the code for a let assignment (it could either be a let assert or
502502-/// just a let, either cases are handled correctly by the decision
503503-/// tree!)
504504-///
505505-pub fn let_<'a>(
506506- compiled_case: &'a CompiledCase,
507507- subject: &'a TypedExpr,
508508- kind: &'a AssignmentKind<TypedExpr>,
509509- expression_generator: &mut Generator<'_, 'a>,
510510- pattern_location: SrcSpan,
511511-) -> Document<'a> {
512512- let scope_position = expression_generator.scope_position.clone();
513513- let mut variables = Variables::new(expression_generator);
514514- let assignment = variables.assign_let_subject(compiled_case, subject);
515515- let assignment_name = assignment.name();
516516- let decision = LetPrinter::new(variables, kind, pattern_location, subject.location())
517517- .decision(assignment_name.clone().to_doc(), &compiled_case.tree);
518518-519519- let doc = docvec![assignments_to_doc(vec![assignment]), decision];
520520- match scope_position {
521521- expression::Position::NotTail(_ordering) => doc,
522522- expression::Position::Tail => docvec![doc, line(), "return ", assignment_name, ";"],
523523- expression::Position::Assign(variable) => {
524524- docvec![doc, line(), variable, " = ", assignment_name, ";"]
525525- }
526526- }
527527-}
528528-529529-struct LetPrinter<'generator, 'module, 'a> {
530530- variables: Variables<'generator, 'module, 'a>,
531531- kind: &'a AssignmentKind<TypedExpr>,
532532- // If the let assert is always going to fail it is redundant and we can
533533- // directly generate an exception instead of first performing some checks
534534- // that we know are going to match.
535535- is_redundant: bool,
536536- pattern_location: SrcSpan,
537537- subject_location: SrcSpan,
538538-}
539539-540540-/// Generating code for a let (or let assert) assignment is quite different from
541541-/// case expressions. A lot of code can be shared between the two but the
542542-/// generated `if-else` code will look a lot different.
543543-///
544544-/// A let is always guaranteed by the type system to succeed, and
545545-/// we don't want to generate any redundant checks for those. At the same time
546546-/// we want to turn a let-assert into a single if statement that looks like this:
547547-///
548548-/// ```gleam
549549-/// let assert Ok(1) = result
550550-/// ```
551551-///
552552-/// ```js
553553-/// if (!result.isOk() || result[0] !== 1) {
554554-/// // throw exception
555555-/// }
556556-///
557557-/// // keep going here...
558558-/// ```
559559-///
560560-/// So we have to throw an exception if any of the checks that we need to perform
561561-/// would fail. Otherwise the let assert is successfull and we can get a hold of
562562-/// the variables that were bound in the pattern.
563563-///
564564-impl<'generator, 'module, 'a> LetPrinter<'generator, 'module, 'a> {
565565- fn new(
566566- variables: Variables<'generator, 'module, 'a>,
534534+ fn assignment_no_match(
535535+ &mut self,
536536+ subject: Document<'a>,
567537 kind: &'a AssignmentKind<TypedExpr>,
568568- pattern_location: SrcSpan,
569538 subject_location: SrcSpan,
570570- ) -> Self {
571571- Self {
572572- variables,
573573- kind,
574574- is_redundant: true,
575575- pattern_location,
576576- subject_location,
577577- }
578578- }
579579-580580- fn decision(&mut self, subject: Document<'a>, decision: &'a Decision) -> Document<'a> {
581581- let Some(ChecksAndBindings { checks, bindings }) =
582582- self.positive_checks_and_bindings(decision)
583583- else {
584584- // In case we never reach a body, we know that this let assert will
585585- // always throw an exception!
586586- self.variables.expression_generator.let_assert_always_panics = true;
587587- return self.assignment_no_match(subject);
588588- };
589589-590590- for (variable, check) in checks.iter() {
591591- self.variables.record_check_assignments(variable, check);
592592- }
593593-594594- let checks = if self.is_redundant {
595595- nil()
596596- } else {
597597- let checks = checks.iter().filter_map(|(variable, check)| {
598598- // Just like with a case expression, we still need to keep track
599599- // of all the variables introduced by successful checks.
600600- self.variables.record_check_assignments(variable, check);
601601- // We then generate a negated version of the runtime check: if
602602- // any of these evaluates to false we know we can throw an
603603- // exception.
604604- match check {
605605- // We never generate runtime checks for tuples, so we skip
606606- // those altogether here.
607607- RuntimeCheck::Tuple { .. } => None,
608608- RuntimeCheck::Variant { .. } if variable.type_.is_nil() => None,
609609- _ => Some(self.variables.runtime_check(
610610- variable,
611611- check,
612612- CheckNegation::Negated,
613613- )),
614614- }
615615- });
616616-617617- docvec![break_("", ""), join(checks, break_(" ||", " || "))]
618618- .nest(INDENT)
619619- .append(break_("", ""))
620620- .group()
621621- };
622622-623623- let doc = if checks.is_empty() {
624624- nil()
625625- } else {
626626- let exception = self.assignment_no_match(subject);
627627- docvec!["if (", checks, ") ", break_block(exception)]
628628- };
629629-630630- let body_bindings = bindings
631631- .iter()
632632- .map(|(name, value)| self.variables.body_binding_doc(name, value));
633633- let body_bindings = join(body_bindings, line());
634634- join_with_line(doc, body_bindings)
635635- }
636636-637637- fn assignment_no_match(&mut self, subject: Document<'a>) -> Document<'a> {
539539+ pattern_location: SrcSpan,
540540+ ) -> Document<'a> {
638541 let AssignmentKind::Assert {
639542 location, message, ..
640640- } = self.kind
543543+ } = kind
641544 else {
642545 unreachable!("inexhaustive let made it to code generation");
643546 };
···655558 [
656559 ("value", subject),
657560 ("start", location.start.to_doc()),
658658- ("end", self.subject_location.end.to_doc()),
659659- ("pattern_start", self.pattern_location.start.to_doc()),
660660- ("pattern_end", self.pattern_location.end.to_doc()),
561561+ ("end", subject_location.end.to_doc()),
562562+ ("pattern_start", pattern_location.start.to_doc()),
563563+ ("pattern_end", pattern_location.end.to_doc()),
661564 ],
662565 )
663566 }
664664-665665- /// A let decision tree has a very precise structure since it's made of a
666666- /// single pattern. It will always be a very narrow tree that has a singe
667667- /// success node with a series of checks leading down to it. If any of those
668668- /// checks fails it immediately leads to a `Fail` node that has to result
669669- /// in an exception. For example:
670670- ///
671671- /// ```gleam
672672- /// let assert [1, 2, ..] = list
673673- /// ```
674674- ///
675675- /// Will have to check that the first item is `1` and the second one is `2`,
676676- /// if any of those checks fail the entire assignment fails.
677677- ///
678678- /// So we can traverse such a decision tree and collect the sequence of checks
679679- /// that need to succeed in order for the assignment to succeed. We also return
680680- /// all the bindings that we discover in the final `Run` block so they can be
681681- /// used by later code generation steps without having to traverse the whole
682682- /// decision tree once again!
683683- ///
684684- /// If there's no `Run` node it means that this pattern will _always_ fail
685685- /// and there's no useful data we could ever return.
686686- /// This could happen due to variant inference (e.g. `let assert Wibble = Wobble`),
687687- /// in that case the assert is redundant.
688688- ///
689689- fn positive_checks_and_bindings(
690690- &mut self,
691691- decision: &'a Decision,
692692- ) -> Option<ChecksAndBindings<'a>> {
693693- let ChecksAndBindings {
694694- mut checks,
695695- bindings,
696696- } = self.checks_and_bindings_loop(decision)?;
697697-698698- // Now we try and reduce the number of size checks that bit array patterns
699699- // need to perform. In particular if all size checks do not depend on any
700700- // previous segment then we can remove all the size checks except the last
701701- // one, as it implies all the other ones!
702702-703703- // First, we create a map from variable IDs to information about the
704704- // size check we need to perform, as different bit array variables must
705705- // still be checked separately.
706706- let mut size_checks = HashMap::new();
707707- // Since we are removing some checks, the check list will get shorter,
708708- // shifting all the indices down. This variable keeps track of the current
709709- // offset so we can correctly calculate the new index of each check.
710710- let mut index_offset = 0;
711711-712712- for (index, (variable, check)) in checks.iter().enumerate() {
713713- if !check.referenced_segment_patterns().is_empty() {
714714- // If any of the checks does reference a previous segment then
715715- // it's not safe to remove intermediate checks! In that case we
716716- // do not try and perform any optimisation and return all the
717717- // checks as they are.
718718- return Some(ChecksAndBindings { checks, bindings });
719719- }
720720-721721- if let RuntimeCheck::BitArray {
722722- test: BitArrayTest::Size(_),
723723- } = check
724724- {
725725- match size_checks.get_mut(&variable.id) {
726726- // If this is the first occurrence of a size check for this
727727- // variable, we record its position as well as the other
728728- // information.
729729- None => {
730730- _ = size_checks.insert(
731731- variable.id,
732732- SizeCheck {
733733- first_occurrence: index - index_offset,
734734- variable: variable.clone(),
735735- check,
736736- },
737737- )
738738- }
739739- // If another size check has already appeared, we simply replace
740740- // the check itself, as the later one is a more restrictive check.
741741- Some(size_check) => {
742742- size_check.check = *check;
743743- // We also increment the offset as the previous size check will
744744- // be removed, further offsetting any checks that come after this.
745745- index_offset += 1;
746746- }
747747- }
748748- }
749749- }
567567+}
750568751751- if size_checks.is_empty() {
752752- // If there's no size test at all, then there's no meaningful optimisation
753753- // we can apply!
754754- return Some(ChecksAndBindings { checks, bindings });
755755- };
756756-757757- checks.retain(|(_variable, check)| {
758758- if let RuntimeCheck::BitArray {
759759- test: BitArrayTest::Size(_),
760760- } = check
761761- {
762762- false
763763- } else {
764764- true
765765- }
766766- });
767767-768768- let mut size_checks = size_checks.into_values().collect_vec();
769769- // We must insert the size checks in the order they appear, to ensure the
770770- // correct ordering of the final checks.
771771- size_checks.sort_by_key(|check| check.first_occurrence);
569569+pub fn let_<'a>(
570570+ compiled_case: &'a CompiledCase,
571571+ subject: &'a TypedExpr,
572572+ kind: &'a AssignmentKind<TypedExpr>,
573573+ expression_generator: &mut Generator<'_, 'a>,
574574+ pattern: &'a TypedPattern,
575575+) -> Document<'a> {
576576+ let _ = pattern;
577577+ let scope_position = expression_generator.scope_position.clone();
578578+ let mut variables = Variables::new(expression_generator, true);
772579773773- for size_check in size_checks {
774774- checks.insert(
775775- size_check.first_occurrence,
776776- (size_check.variable, size_check.check),
777777- );
778778- }
580580+ let assignment = variables.assign_let_subject(compiled_case, subject);
581581+ let assignment_name = assignment.name();
779582780780- Some(ChecksAndBindings { checks, bindings })
583583+ let decision = CasePrinter {
584584+ variables,
585585+ kind: DecisionKind::LetAssert {
586586+ kind,
587587+ subject_location: subject.location(),
588588+ pattern_location: pattern.location(),
589589+ subject: assignment_name.clone(),
590590+ },
781591 }
592592+ .decision(&compiled_case.tree);
782593783783- fn checks_and_bindings_loop(
784784- &mut self,
785785- decision: &'a Decision,
786786- ) -> Option<ChecksAndBindings<'a>> {
787787- match decision {
788788- Decision::Run { body, .. } => Some(ChecksAndBindings::new(&body.bindings)),
789789- Decision::Guard { .. } => unreachable!("guard in let assert decision tree"),
790790- Decision::Fail => {
791791- // If the check could fail it means that the entire let is not
792792- // redundant!
793793- self.is_redundant = false;
794794- None
795795- }
796796- Decision::Switch {
797797- var,
798798- choices,
799799- fallback,
800800- fallback_check,
801801- } => {
802802- let mut result = None;
594594+ let beginning_assignments = pattern.bound_variables().into_iter().map(|variable| {
595595+ docvec![
596596+ "let ",
597597+ expression_generator.local_var(&variable),
598598+ ";",
599599+ line()
600600+ ]
601601+ });
803602804804- // We go over all the decision to record all the bindings and
805805- // see if we can get to a failing node. We will only keep the
806806- // results coming from the positive path!
807807- for (check, decision) in choices {
808808- if let Some(mut checks) = self.checks_and_bindings_loop(decision) {
809809- checks.add_check(var.clone(), check);
810810- result = Some(checks);
811811- };
812812- }
603603+ let doc = docvec![
604604+ assignments_to_doc(vec![assignment]),
605605+ concat(beginning_assignments),
606606+ decision.into_doc()
607607+ ];
813608814814- // Important not to forget the fallback check, that's a path we
815815- // might have to go down to as well!
816816- if let Some(mut checks) = self.checks_and_bindings_loop(fallback) {
817817- if let FallbackCheck::RuntimeCheck { check } = fallback_check {
818818- checks.add_check(var.clone(), check);
819819- result = Some(checks);
820820- };
821821- };
822822-823823- result
824824- }
609609+ match scope_position {
610610+ expression::Position::NotTail(_ordering) => doc,
611611+ expression::Position::Tail => docvec![doc, line(), "return ", assignment_name, ";"],
612612+ expression::Position::Assign(variable) => {
613613+ docvec![doc, line(), variable, " = ", assignment_name, ";"]
825614 }
826615 }
827616}
828617829829-#[derive(Debug)]
830830-struct SizeCheck<'a> {
831831- first_occurrence: usize,
832832- variable: Variable,
833833- check: &'a RuntimeCheck,
834834-}
835835-836836-/// The result we get from inspecting a `let`'s decision tree: it contains all
837837-/// the checks that lead down to the only possible successfull `Body` node and
838838-/// the bindings found inside it.
839839-///
840840-struct ChecksAndBindings<'a> {
841841- checks: VecDeque<(Variable, &'a RuntimeCheck)>,
842842- bindings: &'a Vec<(EcoString, BoundValue)>,
843843-}
844844-845845-impl<'a> ChecksAndBindings<'a> {
846846- fn new(bindings: &'a Vec<(EcoString, BoundValue)>) -> Self {
847847- Self {
848848- checks: VecDeque::new(),
849849- bindings,
850850- }
851851- }
852852-853853- fn add_check(&mut self, variable: Variable, check: &'a RuntimeCheck) {
854854- self.checks.push_front((variable, check));
855855- }
856856-}
857857-858618/// This is a useful piece of state that is kept separate from the generator
859619/// itself so we can reuse it both with `case`s and `let`s without rewriting
860620/// everything from scratch.
861621///
862622struct Variables<'generator, 'module, 'a> {
863623 expression_generator: &'generator mut Generator<'module, 'a>,
624624+ use_reassignment: bool,
864625865626 /// All the pattern variables will be assigned a specific value: being bound
866627 /// to a constructor field, tuple element and so on. Pattern variables never
···930691}
931692932693impl<'generator, 'module, 'a> Variables<'generator, 'module, 'a> {
933933- fn new(expression_generator: &'generator mut Generator<'module, 'a>) -> Self {
694694+ fn new(
695695+ expression_generator: &'generator mut Generator<'module, 'a>,
696696+ use_reassignment: bool,
697697+ ) -> Self {
934698 Variables {
935699 expression_generator,
700700+ use_reassignment,
936701 variable_values: HashMap::new(),
937702 scoped_variable_names: HashMap::new(),
938703 segment_values: HashMap::new(),
···1113878 .get_segment_value(variable_name)
1114879 .unwrap_or_else(|| self.read_action_to_doc(bit_array, read_action)),
1115880 };
11161116- let_doc(local_variable_name.clone(), assigned_value)
881881+882882+ if self.use_reassignment {
883883+ reassignment_doc(local_variable_name.clone(), assigned_value)
884884+ } else {
885885+ let_doc(local_variable_name.clone(), assigned_value)
886886+ }
1117887 }
11188881119889 /// Generates the document to perform a (possibly negated) runtime check on
···1123893 &mut self,
1124894 variable: &Variable,
1125895 runtime_check: &'a RuntimeCheck,
11261126- negation: CheckNegation,
1127896 ) -> Document<'a> {
1128897 let value = self.get_value(variable);
112989811301130- let equality = if negation.is_negated() {
11311131- " !== "
11321132- } else {
11331133- " === "
11341134- };
899899+ let equality = " === ";
11359001136901 match runtime_check {
1137902 RuntimeCheck::String { value: expected } => docvec![value, equality, string(expected)],
1138903 RuntimeCheck::Float { value: expected } => docvec![value, equality, float(expected)],
1139904 RuntimeCheck::Int { value: expected } => docvec![value, equality, int(expected)],
1140905 RuntimeCheck::StringPrefix { prefix, .. } => {
11411141- let negation = if negation.is_negated() {
11421142- "!".to_doc()
11431143- } else {
11441144- nil()
11451145- };
11461146-11471147- docvec![negation, value, ".startsWith(", string(prefix), ")"]
906906+ docvec![value, ".startsWith(", string(prefix), ")"]
1148907 }
11499081150909 RuntimeCheck::BitArray { test } => match test {
···1161920 }
11629211163922 BitArrayTest::VariableIsNotNegative { variable } => {
11641164- if negation.is_negated() {
11651165- docvec![self.local_var(variable.name()), " < 0"]
11661166- } else {
11671167- docvec![self.local_var(variable.name()), " >= 0"]
11681168- }
923923+ docvec![self.local_var(variable.name()), " >= 0"]
1169924 }
11709251171926 BitArrayTest::SegmentIsFiniteFloat {
···1187942 };
1188943 let check = self.bit_array_slice_to_float(value, start_doc, end, endianness);
118994411901190- if negation.is_negated() {
11911191- docvec!["!Number.isFinite(", check, ")"]
11921192- } else {
11931193- docvec!["Number.isFinite(", check, ")"]
11941194- }
945945+ docvec!["Number.isFinite(", check, ")"]
1195946 }
11969471197948 // Here we need to make sure that the bit array has a specific
1198949 // size.
1199950 BitArrayTest::Size(SizeTest { operator, size }) => {
1200951 let operator = match operator {
12011201- SizeOperator::GreaterEqual if negation.is_negated() => " < ",
1202952 SizeOperator::GreaterEqual => " >= ",
1203953 SizeOperator::Equal => equality,
1204954 };
···1219969 value,
1220970 expected,
1221971 read_action,
12221222- negation,
1223972 *encoding,
1224973 ),
12251225- BitArrayMatchedValue::LiteralFloat(expected) => self
12261226- .literal_float_segment_bytes_check(value, expected, read_action, negation),
12271227- BitArrayMatchedValue::LiteralInt(expected) => self
12281228- .literal_int_segment_bytes_check(
12291229- value,
12301230- expected.clone(),
12311231- read_action,
12321232- negation,
12331233- ),
974974+ BitArrayMatchedValue::LiteralFloat(expected) => {
975975+ self.literal_float_segment_bytes_check(value, expected, read_action)
976976+ }
977977+ BitArrayMatchedValue::LiteralInt(expected) => {
978978+ self.literal_int_segment_bytes_check(value, expected.clone(), read_action)
979979+ }
1234980 BitArrayMatchedValue::Variable(..)
1235981 | BitArrayMatchedValue::Discard(..)
1236982 | BitArrayMatchedValue::Assign { .. } => {
···1247993 // Some variants like `Bool` and `Result` are special cased and checked
1248994 // in a different way from all other variants.
1249995 RuntimeCheck::Variant { match_, .. } if variable.type_.is_bool() => {
12501250- match (match_.name().as_str(), negation) {
12511251- ("True", CheckNegation::NotNegated) => value.to_doc(),
12521252- ("True", CheckNegation::Negated) => docvec!["!", value],
12531253- (_, CheckNegation::NotNegated) => docvec!["!", value],
12541254- (_, CheckNegation::Negated) => value.to_doc(),
996996+ match match_.name().as_str() {
997997+ "True" => value.to_doc(),
998998+ _ => docvec!["!", value],
1255999 }
12561000 }
12571001···12691013 .map(|module| eco_format!("${module}."))
12701014 .unwrap_or_default();
1271101512721272- let check = docvec![value, " instanceof ", qualification, match_.name()];
12731273- if negation.is_negated() {
12741274- docvec!["!(", check, ")"]
12751275- } else {
12761276- check
12771277- }
10161016+ docvec![value, " instanceof ", qualification, match_.name()]
12781017 }
1279101812801019 RuntimeCheck::NonEmptyList { .. } => {
12811281- if negation.is_negated() {
12821282- self.expression_generator.tracker.list_empty_class_used = true;
12831283- docvec![value, " instanceof $Empty"]
12841284- } else {
12851285- self.expression_generator.tracker.list_non_empty_class_used = true;
12861286- docvec![value, " instanceof $NonEmpty"]
12871287- }
10201020+ self.expression_generator.tracker.list_non_empty_class_used = true;
10211021+ docvec![value, " instanceof $NonEmpty"]
12881022 }
1289102312901024 RuntimeCheck::EmptyList => {
12911291- if negation.is_negated() {
12921292- self.expression_generator.tracker.list_non_empty_class_used = true;
12931293- docvec![value, " instanceof $NonEmpty"]
12941294- } else {
12951295- self.expression_generator.tracker.list_empty_class_used = true;
12961296- docvec![value, " instanceof $Empty"]
12971297- }
10251025+ self.expression_generator.tracker.list_empty_class_used = true;
10261026+ docvec![value, " instanceof $Empty"]
12981027 }
12991028 }
13001029 }
···15251254 bit_array: EcoString,
15261255 literal_string: &EcoString,
15271256 read_action: &ReadAction,
15281528- check_negation: CheckNegation,
15291257 encoding: StringEncoding,
15301258 ) -> Document<'a> {
15311259 let ReadAction {
···15361264 } = read_action;
15371265 let mut checks = vec![];
1538126615391539- let equality = if check_negation.is_negated() {
15401540- " !== "
15411541- } else {
15421542- " === "
15431543- };
12671267+ let equality = " === ";
1544126815451269 let escaped = convert_string_escape_chars(literal_string);
15461270 // We need to have this vector here so that we don't run into lifetime
···15801304 }
15811305 }
1582130615831583- if check_negation.is_negated() {
15841584- // If the check is negated, it fails if any of the byte checks fail.
15851585- join(checks, break_(" ||", " || ")).group()
15861586- } else {
15871587- // Otherwise the check succeeds if all the byte checks succeed.
15881588- join(checks, break_(" &&", " && ")).nest(INDENT).group()
15891589- }
13071307+ // Otherwise the check succeeds if all the byte checks succeed.
13081308+ join(checks, break_(" &&", " && ")).nest(INDENT).group()
15901309 }
1591131015921311 /// This generates all the checks that need to be performed to make sure a
···15991318 bit_array: EcoString,
16001319 literal_int: BigInt,
16011320 read_action: &ReadAction,
16021602- check_negation: CheckNegation,
16031321 ) -> Document<'a> {
16041322 let ReadAction {
16051323 from: start,
···16091327 ..
16101328 } = read_action;
1611132916121612- let equality = if check_negation.is_negated() {
16131613- " !== "
16141614- } else {
16151615- " === "
16161616- };
13301330+ let equality = " === ";
1617133116181332 if let (Some(mut from_byte), Some(size)) = (start.constant_bytes(), size.constant_bytes()) {
16191333 // If the number starts at a byte-aligned offset and is made of a
···16261340 from_byte += 1;
16271341 }
1628134216291629- if check_negation.is_negated() {
16301630- join(checks, break_(" ||", " || ")).group()
16311631- } else {
16321632- join(checks, break_(" &&", " && ")).nest(INDENT).group()
16331633- }
13431343+ join(checks, break_(" &&", " && ")).nest(INDENT).group()
16341344 } else {
16351345 // Otherwise we have to take an int slice out of the bit array and
16361346 // check it matches the expected value.
···16571367 bit_array: EcoString,
16581368 expected: &EcoString,
16591369 read_action: &ReadAction,
16601660- check_negation: CheckNegation,
16611370 ) -> Document<'a> {
16621371 let ReadAction {
16631372 from: start,
···16661375 ..
16671376 } = read_action;
1668137716691669- let equality = if check_negation.is_negated() {
16701670- " !== "
16711671- } else {
16721672- " === "
16731673- };
13781378+ let equality = " === ";
1674137916751380 // Unlike literal integers and strings, for now we don't try and apply any
16761381 // optimisation in the way we match on those: we take an entire slice,
···17991504 }
18001505}
1801150618021802-#[derive(Eq, PartialEq)]
18031803-/// Wether a runtime check should be negated or not.
18041804-///
18051805-enum CheckNegation {
18061806- Negated,
18071807- NotNegated,
18081808-}
18091809-18101810-impl CheckNegation {
18111811- fn is_negated(&self) -> bool {
18121812- match self {
18131813- CheckNegation::Negated => true,
18141814- CheckNegation::NotNegated => false,
18151815- }
18161816- }
18171817-}
18181818-18191507/// When going over the subjects of a case expression/let we might end up in two
18201508/// situation: the subject might be a variable or it could be a more complex
18211509/// expression (like a function call, a complex expression, ...).
···19301618 } else {
19311619 docvec![one, line(), other]
19321620 }
16211621+}
16221622+16231623+fn reassignment_doc(variable_name: EcoString, value: Document<'_>) -> Document<'_> {
16241624+ docvec![variable_name, " = ", value, ";"]
19331625}
1934162619351627fn let_doc(variable_name: EcoString, value: Document<'_>) -> Document<'_> {