diff --git a/packages/server/src/pre/pruner.rs b/packages/server/src/pre/pruner.rs index fe669e14..fa8f6451 100644 --- a/packages/server/src/pre/pruner.rs +++ b/packages/server/src/pre/pruner.rs @@ -94,6 +94,130 @@ impl Pruner { (text, map, replacements, stream.into_inner()) } + + fn eval_branch(&mut self, branch: &Branch) -> Value { + use DirectiveKind::*; + + match branch.directive.kind() { + directive @ (IfDef | IfNDef | ElseIfDef | ElseIfNDef) => { + let is_defined = match branch.condition.as_ref() { + Some(expr) => self.eval_is_defined(expr), + None => { + self.errors.push(SpannedError { + message: "ifdef must be followed by an identifier".into(), + source: self.source.clone(), + span: Some(branch.directive.span()), + }); + + return Value::Bool(false); + } + }; + + match directive { + IfDef | ElseIfDef => Value::Bool(is_defined), + IfNDef | ElseIfNDef => Value::Bool(!is_defined), + _ => unreachable!(), + } + } + Else => { + // If we've made it here, it's because the corresponding #if[n?def] + // evaluation was `false` + Value::Bool(true) + } + If | ElseIf => { + if let Some(condition) = branch.condition.as_ref() { + self.eval_expr(condition) + } else { + self.errors.push(SpannedError { + message: "if must be followed by an expression".into(), + source: self.source.clone(), + span: Some(branch.directive.span()), + }); + + Value::Bool(false) + } + } + Endif => { + self.errors.push(SpannedError { + message: "Unexpected `#endif`".into(), + source: self.source.clone(), + span: Some(branch.directive.span()), + }); + + Value::Bool(false) + } + } + } + + fn eval_is_defined(&mut self, expr: &Expr) -> bool { + let symbol = match expr { + Expr::Ident(IdentExpr::Leaf(token)) => token.lexeme(), + Expr::Primary(inner) => match inner.expr.as_ref() { + Expr::Ident(IdentExpr::Leaf(token)) => token.lexeme(), + other => { + self.errors.push(SpannedError { + message: format!("Expected identifier, found {other:?}"), + source: self.source.clone(), + span: Some(other.span()), + }); + + return false; + } + }, + other => { + self.errors.push(SpannedError { + message: format!("Expected identifier, found {other:?}"), + source: self.source.clone(), + span: Some(other.span()), + }); + + return false; + } + }; + + self.defs.contains_key(symbol.as_str()) + } + + fn eval_expr(&mut self, expr: &Expr) -> Value { + let mut interpreter = + Interpreter::with_defs(self.source.clone(), Arc::new(self.defs.clone())); + expr.walk(&mut interpreter); + + match interpreter.result { + Some(Ok(value)) => value, + Some(Err(err)) => { + self.errors.push(err); + + Value::Bool(false) + } + None => { + self.errors.push(SpannedError { + message: "Failed to evaluate expression".into(), + source: self.source.clone(), + span: Some(expr.span()), + }); + + Value::Bool(false) + } + } + } + + fn mark_inactive_recursive(&mut self, branch: &Branch) { + self.mark_body_inactive(branch); + + if let Some(else_branch) = &branch.else_ { + self.mark_inactive_recursive(else_branch); + } + } + + fn mark_body_inactive(&mut self, branch: &Branch) { + if !branch.body.is_empty() { + let start = branch.body.first().unwrap().span(); + let end = branch.body.last().unwrap().span(); + + self.inactive_spans.push(start.through(end)); + } + } } impl Visitor for Pruner { @@ -140,104 +264,7 @@ impl Visitor for Pruner { } fn visit_branch(&mut self, branch: &Branch) -> FlowControl { - use DirectiveKind::*; - - let eval = 'eval: { - match branch.directive.kind() { - directive @ (IfDef | IfNDef | ElseIfDef | ElseIfNDef) => { - let symbol = match branch.condition.as_ref() { - Some(Expr::Ident(IdentExpr::Leaf(token))) => token.lexeme(), - Some(Expr::Primary(inner)) => match inner.expr.as_ref() { - Expr::Ident(IdentExpr::Leaf(token)) => token.lexeme(), - other => { - self.errors.push(SpannedError { - message: format!("Expected identifier, found {other:?}"), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - break 'eval Value::Bool(false); - } - }, - Some(other) => { - self.errors.push(SpannedError { - message: format!("Expected identifier, found {other:?}"), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - break 'eval Value::Bool(false); - } - None => { - self.errors.push(SpannedError { - message: "ifdef must be followed by an identifier".into(), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - break 'eval Value::Bool(false); - } - }; - let is_defined = self.defs.contains_key(symbol.as_str()); - - match directive { - IfDef | ElseIfDef => Value::Bool(is_defined), - IfNDef | ElseIfNDef => Value::Bool(!is_defined), - _ => unreachable!(), - } - } - Else => { - // If we've made it here, it's because the corresponding #if[n?def] - // evaluation was `false` - Value::Bool(true) - } - If | ElseIf => { - if let Some(condition) = branch.condition.as_ref() { - let mut interpreter = Interpreter::with_defs( - self.source.clone(), - Arc::new(self.defs.clone()), - ); - condition.walk(&mut interpreter); - - match interpreter.result { - Some(Ok(value)) => value, - Some(Err(err)) => { - self.errors.push(err); - - Value::Bool(false) - } - None => { - self.errors.push(SpannedError { - message: "Failed to evaluate expression".into(), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - Value::Bool(false) - } - } - } else { - self.errors.push(SpannedError { - message: "Expected expression".into(), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - Value::Bool(false) - } - } - Endif => { - self.errors.push(SpannedError { - message: "Unexpected `#endif`".into(), - source: self.source.clone(), - span: Some(branch.directive.span()), - }); - - return FlowControl::Break; - } - } - }; - + let eval = self.eval_branch(branch); let coerced = match eval { Value::Bool(value) => value, Value::Float(f) if f == 0.0 => false, @@ -247,24 +274,14 @@ impl Visitor for Pruner { if coerced { if let Some(else_branch) = &branch.else_ { - if !else_branch.body.is_empty() { - let start = else_branch.body.first().unwrap().span(); - let end = else_branch.body.last().unwrap().span(); - - self.inactive_spans.push(start.through(end)); - } + self.mark_inactive_recursive(else_branch); } for section in branch.body.iter() { section.walk(self); } } else { - if !branch.body.is_empty() { - let start = branch.body.first().unwrap().span(); - let end = branch.body.last().unwrap().span(); - - self.inactive_spans.push(start.through(end)); - } + self.mark_body_inactive(branch); if let Some(else_branch) = &branch.else_ { else_branch.walk(self);