use full_moon::{
ast, parse,
tokenizer::*,
visitors::{Visitor, VisitorMut},
};
#[test]
fn test_visitor() {
struct FunctionCallVisitor {
called: Vec<String>,
}
impl Visitor for FunctionCallVisitor {
fn visit_function_call(&mut self, call: &ast::FunctionCall) {
match call.prefix() {
ast::Prefix::Name(token) => {
self.called.push(token.to_string());
}
_ => unreachable!(),
}
}
}
let code = parse("foo(bar())").unwrap();
let mut visitor = FunctionCallVisitor { called: Vec::new() };
visitor.visit_ast(&code);
assert_eq!(visitor.called, vec!["foo", "bar"]);
}
#[test]
fn test_visitor_mut() {
struct SnakeNamer;
impl VisitorMut for SnakeNamer {
fn visit_local_assignment(
&mut self,
assignment: ast::LocalAssignment,
) -> ast::LocalAssignment {
let name_list = assignment
.names()
.pairs()
.map(|name| {
name.to_owned().map(|value| {
value.with_token(Token::new(TokenType::Identifier {
identifier: value.token().to_string().replace('s', "sss").into(),
}))
})
})
.collect();
assignment.with_names(name_list)
}
}
let code = parse("local dogs, snakes = 1").unwrap();
let code = SnakeNamer.visit_ast(code);
assert_eq!(code.to_string(), "local dogsss, sssnakesss = 1");
struct PositionValidator;
impl Visitor for PositionValidator {
fn visit_local_assignment(&mut self, assignment: &ast::LocalAssignment) {
for name in assignment.names() {
assert_eq!(
name.end_position().bytes() - name.start_position().bytes(),
name.token().to_string().len()
);
}
}
}
let code = code.update_positions();
PositionValidator.visit_ast(&code);
}
#[test]
fn test_visit_token() {
#[derive(Default)]
struct CommentVisitor {
comments: Vec<String>,
}
impl Visitor for CommentVisitor {
fn visit_single_line_comment(&mut self, token: &Token) {
self.comments.push(token.to_string());
}
}
let mut visitor = CommentVisitor::default();
let code = parse(
r#"
-- bla bla bla
--[[
multi line comment
]]
-- comment here
local x = 1
-- and here
"#,
)
.unwrap();
visitor.visit_ast(&code);
assert_eq!(
visitor.comments,
vec!["-- bla bla bla", "-- comment here", "-- and here"]
);
}
#[test]
fn test_end_visit() {
#[derive(Default)]
struct LogVisitor {
instructions: usize,
if_start_at: usize,
if_end_at: usize,
called_at: usize,
}
impl Visitor for LogVisitor {
fn visit_if(&mut self, _: &ast::If) {
self.instructions += 1;
self.if_start_at = self.instructions
}
fn visit_if_end(&mut self, _: &ast::If) {
self.instructions += 1;
self.if_end_at = self.instructions;
}
fn visit_call(&mut self, _: &ast::Call) {
self.instructions += 1;
self.called_at = self.instructions;
}
}
let mut visitor = LogVisitor::default();
visitor.visit_ast(
&parse(
r#"
if true then
call()
end
"#,
)
.unwrap(),
);
assert_eq!(visitor.if_start_at, 1);
assert_eq!(visitor.called_at, 2);
assert_eq!(visitor.if_end_at, 3);
}
#[test]
fn test_unary_visitor_regression() {
struct TestVisitor(bool);
impl Visitor for TestVisitor {
fn visit_un_op(&mut self, _: &ast::UnOp) {
self.0 = true;
}
}
let mut visitor = TestVisitor(false);
visitor.visit_ast(&parse("local x = #{}").unwrap());
assert!(visitor.0, "Unary operation was not visited");
}