use std::collections::{HashMap, HashSet};
use crate::{
CompilationError, SourceSpan,
ast::{
self, ASTVisitor, Add, Binding, Constant, CustomDice, Div, DropHighest,
DropLowest, Exp, Group, Mod, Mul, Neg, Range, StandardDice, Sub,
Variable
}
};
#[derive(Clone, Debug, Default)]
pub struct Validator<'a, 'src>
{
parameters: HashMap<&'a str, SourceSpan>,
bindings: HashMap<&'a str, SourceSpan>,
bound: HashSet<&'a str>,
forward: Vec<&'a Variable<'src>>,
referenced: HashSet<&'a str>
}
impl Validator<'_, '_>
{
#[inline]
pub fn new() -> Self { Self::default() }
pub fn validate<'src>(
ast: &ast::Function<'src>
) -> Result<(), CompilationError<'src>>
{
ast.accept(&mut Validator::new())
}
}
impl<'a, 'src: 'a> ASTVisitor<'a, 'src> for Validator<'a, 'src>
{
type Error = CompilationError<'src>;
type Output = ();
fn enter_function(
&mut self,
node: &'a ast::Function<'src>
) -> Result<(), Self::Error>
{
*self = Self::default();
if let Some(ref parameters) = node.parameters
{
for param in parameters
{
if let Some(&first) = self.parameters.get(&*param.name)
{
return Err(CompilationError::DuplicateParameter {
name: param.name.clone(),
first,
duplicate: param.span
});
}
self.parameters.insert(¶m.name, param.span);
}
}
Ok(())
}
fn enter_binding(
&mut self,
node: &'a Binding<'src>
) -> Result<(), Self::Error>
{
if let Some(¶meter) = self.parameters.get(&*node.name)
{
return Err(CompilationError::BindingCollidesWithParameter {
name: node.name.clone(),
parameter,
binding: node.name_span
});
}
if let Some(&first) = self.bindings.get(&*node.name)
{
return Err(CompilationError::DuplicateBinding {
name: node.name.clone(),
first,
duplicate: node.name_span
});
}
self.bindings.insert(&node.name, node.name_span);
Ok(())
}
fn visit_function(
&mut self,
_node: &'a ast::Function<'src>,
_body: ()
) -> Result<(), Self::Error>
{
match self.forward.iter().find_map(|reference| {
self.bindings
.get(&*reference.name)
.map(|&binding| (reference, binding))
})
{
Some((reference, binding)) =>
{
Err(CompilationError::UseBeforeBind {
name: reference.name.clone(),
reference: reference.span,
binding
})
},
None => Ok(())
}
}
fn visit_group(
&mut self,
_node: &'a Group<'src>,
_expression: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_constant(&mut self, _node: &'a Constant)
-> Result<(), Self::Error>
{
Ok(())
}
fn visit_variable(
&mut self,
node: &'a Variable<'src>
) -> Result<(), Self::Error>
{
if !self.bound.contains(&*node.name)
&& self.referenced.insert(&node.name)
{
self.forward.push(node);
}
Ok(())
}
fn visit_binding(
&mut self,
node: &'a Binding<'src>,
_expression: ()
) -> Result<(), Self::Error>
{
self.bound.insert(&node.name);
Ok(())
}
fn visit_range(
&mut self,
_node: &'a Range<'src>,
_start: (),
_end: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_standard_dice(
&mut self,
_node: &'a StandardDice<'src>,
_count: (),
_faces: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_custom_dice(
&mut self,
_node: &'a CustomDice<'src>,
_count: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_drop_lowest(
&mut self,
_node: &'a DropLowest<'src>,
_dice: (),
_drop: Option<()>
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_drop_highest(
&mut self,
_node: &'a DropHighest<'src>,
_dice: (),
_drop: Option<()>
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_add(
&mut self,
_node: &'a Add<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_sub(
&mut self,
_node: &'a Sub<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_mul(
&mut self,
_node: &'a Mul<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_div(
&mut self,
_node: &'a Div<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_mod(
&mut self,
_node: &'a Mod<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_exp(
&mut self,
_node: &'a Exp<'src>,
_left: (),
_right: ()
) -> Result<(), Self::Error>
{
Ok(())
}
fn visit_neg(
&mut self,
_node: &'a Neg<'src>,
_operand: ()
) -> Result<(), Self::Error>
{
Ok(())
}
}