Skip to content

Commit

Permalink
Cleanup branch evaluation; Fix inactive markers
Browse files Browse the repository at this point in the history
  • Loading branch information
dannymcgee committed May 16, 2024
1 parent f1e447e commit a28cda5
Showing 1 changed file with 127 additions and 110 deletions.
237 changes: 127 additions & 110 deletions packages/server/src/pre/pruner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down

0 comments on commit a28cda5

Please sign in to comment.