mod gen { mod csvlexer; mod csvlistener; mod csvparser; mod csvvisitor; mod visitorbasiclexer; mod visitorbasiclistener; mod visitorbasicparser; mod visitorbasicvisitor; mod visitorcalclexer; mod visitorcalclistener; mod visitorcalcparser; mod visitorcalcvisitor; use crate::gen::csvparser::CSVParserContextType; use crate::gen::visitorbasiclexer::VisitorBasicLexer; use crate::gen::visitorbasicparser::{VisitorBasicParser, VisitorBasicParserContextType}; use crate::gen::visitorbasicvisitor::VisitorBasicVisitorCompat; use crate::gen::visitorcalclexer::VisitorCalcLexer; use antlr_rust::common_token_stream::CommonTokenStream; use antlr_rust::parser::ParserNodeType; use antlr_rust::token::Token; use antlr_rust::tree::{ErrorNode, ParseTree, ParseTreeVisitorCompat, TerminalNode, Visitable}; use antlr_rust::InputStream; use visitorcalcparser::{ AddContext, AddContextAttrs, MultiplyContext, MultiplyContextAttrs, NumberContext, NumberContextAttrs, SContext, SContextAttrs, VisitorCalcParser, VisitorCalcParserContextType, }; use visitorcalcvisitor::VisitorCalcVisitorCompat; #[test] fn test_visit_terminal_node() { let mut lexer = VisitorBasicLexer::new(InputStream::new("A".into())); let mut parser = VisitorBasicParser::new(CommonTokenStream::new(lexer)); let root = parser.s().unwrap(); assert_eq!("(s A )", root.to_string_tree(&*parser)); struct TestVisitor(String); impl ParseTreeVisitorCompat<'_> for TestVisitor { type Node = VisitorBasicParserContextType; type Return = String; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn visit_terminal(&mut self, _node: &TerminalNode<'_, Self::Node>) -> Self::Return { _node.symbol.to_string() + "\n" } fn aggregate_results( &self, aggregate: Self::Return, next: Self::Return, ) -> Self::Return { aggregate + &next } } impl VisitorBasicVisitorCompat<'_> for TestVisitor {} let result = TestVisitor(String::new()).visit(&*root); let expected = "[@0,0:0='A',<1>,1:0]\n\ [@1,1:0='',<-1>,1:1]\n"; assert_eq!(result, expected) } #[test] fn test_visit_error_node() { let mut lexer = VisitorBasicLexer::new(InputStream::new("".into())); let mut parser = VisitorBasicParser::new(CommonTokenStream::new(lexer)); let root = parser.s().unwrap(); assert_eq!("(s )", root.to_string_tree(&*parser)); struct TestVisitor(String); impl ParseTreeVisitorCompat<'_> for TestVisitor { type Node = VisitorBasicParserContextType; type Return = String; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn visit_error_node(&mut self, _node: &ErrorNode<'_, Self::Node>) -> Self::Return { format!("Error encountered: {}", _node.symbol) } fn aggregate_results( &self, aggregate: Self::Return, next: Self::Return, ) -> Self::Return { aggregate + &next } } impl VisitorBasicVisitorCompat<'_> for TestVisitor {} let result = TestVisitor(String::new()).visit(&*root); let expected = "Error encountered: [@-1,-1:-1='',<1>,1:0]"; assert_eq!(result, expected) } #[test] fn test_should_not_visit_EOF() { let mut lexer = VisitorBasicLexer::new(InputStream::new("A".into())); let mut parser = VisitorBasicParser::new(CommonTokenStream::new(lexer)); let root = parser.s().unwrap(); assert_eq!("(s A )", root.to_string_tree(&*parser)); struct TestVisitor(String); impl ParseTreeVisitorCompat<'_> for TestVisitor { type Node = VisitorBasicParserContextType; type Return = String; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn visit_terminal(&mut self, _node: &TerminalNode<'_, Self::Node>) -> Self::Return { _node.symbol.to_string() + "\n" } fn should_visit_next_child( &self, node: &>::Type, current: &Self::Return, ) -> bool { current.is_empty() } } impl VisitorBasicVisitorCompat<'_> for TestVisitor {} let result = TestVisitor(String::new()).visit(&*root); let expected = "[@0,0:0='A',<1>,1:0]\n"; assert_eq!(result, expected); struct TestVisitorUnit(String); impl ParseTreeVisitorCompat<'_> for TestVisitorUnit { type Node = VisitorBasicParserContextType; type Return = (); fn temp_result(&mut self) -> &mut Self::Return { Box::leak(Box::new(())) } fn visit_terminal(&mut self, _node: &TerminalNode<'_, Self::Node>) -> Self::Return { self.0 += &_node.symbol.to_string(); } } impl VisitorBasicVisitorCompat<'_> for TestVisitorUnit {} } #[test] fn test_should_not_visit_anything() { let mut lexer = VisitorBasicLexer::new(InputStream::new("A".into())); let mut parser = VisitorBasicParser::new(CommonTokenStream::new(lexer)); let root = parser.s().unwrap(); assert_eq!("(s A )", root.to_string_tree(&*parser)); struct TestVisitor(String); impl ParseTreeVisitorCompat<'_> for TestVisitor { type Node = VisitorBasicParserContextType; type Return = String; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn visit_terminal(&mut self, _node: &TerminalNode<'_, Self::Node>) -> Self::Return { unreachable!() } fn should_visit_next_child( &self, node: &>::Type, current: &Self::Return, ) -> bool { false } } impl VisitorBasicVisitorCompat<'_> for TestVisitor {} let result = TestVisitor(String::new()).visit(&*root); let expected = ""; assert_eq!(result, expected) } #[test] fn test_visitor_with_return() { struct CalcVisitor(isize); impl ParseTreeVisitorCompat<'_> for CalcVisitor { type Node = VisitorCalcParserContextType; type Return = isize; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn aggregate_results( &self, aggregate: Self::Return, next: Self::Return, ) -> Self::Return { panic!("Should not be reachable") } } impl VisitorCalcVisitorCompat<'_> for CalcVisitor { fn visit_s(&mut self, ctx: &SContext<'_>) -> Self::Return { self.visit(&*ctx.expr().unwrap()) } fn visit_add(&mut self, ctx: &AddContext<'_>) -> Self::Return { let left = self.visit(&*ctx.expr(0).unwrap()); let right = self.visit(&*ctx.expr(1).unwrap()); if ctx.ADD().is_some() { left + right } else { left - right } } fn visit_number(&mut self, ctx: &NumberContext<'_>) -> Self::Return { ctx.INT().unwrap().get_text().parse().unwrap() } fn visit_multiply(&mut self, ctx: &MultiplyContext<'_>) -> Self::Return { let left = self.visit(&*ctx.expr(0).unwrap()); let right = self.visit(&*ctx.expr(1).unwrap()); if ctx.MUL().is_some() { left * right } else { left / right } } } let mut _lexer = VisitorCalcLexer::new(InputStream::new("2 + 8 / 2".into())); let token_source = CommonTokenStream::new(_lexer); let mut parser = VisitorCalcParser::new(token_source); let root = parser.s().unwrap(); assert_eq!( "(s (expr (expr 2) + (expr (expr 8) / (expr 2))) )", root.to_string_tree(&*parser) ); let mut visitor = CalcVisitor(0); let visitor_result = visitor.visit(&*root); assert_eq!(6, visitor_result) } // tests zero-copy parsing with non static visitor #[test] fn test_visitor_retrieve_reference() { use antlr_rust::token_factory::ArenaCommonFactory; use antlr_rust::tree::ParseTreeVisitor; use csvlexer::CSVLexer; use csvparser::{CSVParser, CsvFileContext, HdrContext, RowContext, RowContextAttrs}; use csvvisitor::CSVVisitor; use std::borrow::Cow; use std::rc::Rc; // `T` here to ensure that visitor can have lifetime shorter that `'input` string struct MyCSVVisitor<'i, T>(Vec<&'i str>, T); impl<'i, T> ParseTreeVisitor<'i, CSVParserContextType> for MyCSVVisitor<'i, T> { fn visit_terminal(&mut self, node: &TerminalNode<'i, CSVParserContextType>) { if node.symbol.get_token_type() == csvparser::TEXT { if let Cow::Borrowed(s) = node.symbol.text { self.0.push(s); } } } } impl<'i, T> CSVVisitor<'i> for MyCSVVisitor<'i, T> { fn visit_hdr(&mut self, _ctx: &HdrContext<'i>) {} fn visit_row(&mut self, ctx: &RowContext<'i>) { if ctx.field_all().len() > 1 { self.visit_children(ctx) } } } fn parse<'a>(tf: &'a ArenaCommonFactory<'a>) -> Rc> { let mut _lexer = CSVLexer::new_with_token_factory(InputStream::new("h1,h2\nd1,d2\nd3\n".into()), tf); let token_source = CommonTokenStream::new(_lexer); let mut parser = CSVParser::new(token_source); let result = parser.csvFile().expect("parsed unsuccessfully"); let mut test = 5; let mut visitor = MyCSVVisitor(Vec::new(), &mut test); result.accept(&mut visitor); assert_eq!(visitor.0, vec!["d1", "d2"]); result } let tf = ArenaCommonFactory::default(); let _result = parse(&tf); } #[test] fn test_visitor_retrieve_reference_by_return() { use antlr_rust::token_factory::ArenaCommonFactory; use csvlexer::CSVLexer; use csvparser::{CSVParser, CsvFileContext, HdrContext, RowContext, RowContextAttrs}; use csvvisitor::CSVVisitorCompat; use std::borrow::Cow; use std::rc::Rc; struct MyCSVVisitor<'i>(Vec<&'i str>); impl<'i> ParseTreeVisitorCompat<'i> for MyCSVVisitor<'i> { type Node = CSVParserContextType; type Return = Vec<&'i str>; fn temp_result(&mut self) -> &mut Self::Return { &mut self.0 } fn visit_terminal( &mut self, node: &TerminalNode<'i, CSVParserContextType>, ) -> Self::Return { if node.symbol.get_token_type() == csvparser::TEXT { if let Cow::Borrowed(s) = node.symbol.text { return vec![s]; } } vec![] } fn aggregate_results( &self, mut aggregate: Self::Return, next: Self::Return, ) -> Self::Return { aggregate.extend(next); aggregate } } impl<'i> CSVVisitorCompat<'i> for MyCSVVisitor<'i> { fn visit_hdr(&mut self, _ctx: &HdrContext<'i>) -> Self::Return { vec![] } fn visit_row(&mut self, ctx: &RowContext<'i>) -> Self::Return { if ctx.field_all().len() > 1 { self.visit_children(ctx) } else { vec![] } } } fn parse<'a>(tf: &'a ArenaCommonFactory<'a>) -> Rc> { let mut _lexer = CSVLexer::new_with_token_factory(InputStream::new("h1,h2\nd1,d2\nd3\n".into()), tf); let token_source = CommonTokenStream::new(_lexer); let mut parser = CSVParser::new(token_source); let result = parser.csvFile().expect("parsed unsuccessfully"); let mut visitor = MyCSVVisitor(Vec::new()); let visitor_result = visitor.visit(&*result); assert_eq!(visitor_result, vec!["d1", "d2"]); result } let tf = ArenaCommonFactory::default(); let _result = parse(&tf); } }