From ef5cb279a355ef1c8199aef98bfc821e30370b10 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Thu, 30 Apr 2020 12:23:19 +0200 Subject: [PATCH] Generating SpanTree from macros (https://github.com/enso-org/ide/pull/389) Before during SpanTree generation we treated macros as a leaves. Now we properly look into them, get all the AST nodes in their patterns and generate children of these SpanTree nodes from them. Original commit: https://github.com/enso-org/ide/commit/aeff31f2d88bd2f791b441ac92cd759637935f97 --- gui/src/rust/ide/ast/impl/src/lib.rs | 2 +- gui/src/rust/ide/span-tree/src/action.rs | 135 ++++---- gui/src/rust/ide/span-tree/src/generate.rs | 318 +++++++++++++----- .../rust/ide/span-tree/src/generate/macros.rs | 142 ++++++++ gui/src/rust/ide/span-tree/src/iter.rs | 14 +- gui/src/rust/ide/span-tree/src/node.rs | 22 +- 6 files changed, 475 insertions(+), 158 deletions(-) create mode 100644 gui/src/rust/ide/span-tree/src/generate/macros.rs diff --git a/gui/src/rust/ide/ast/impl/src/lib.rs b/gui/src/rust/ide/ast/impl/src/lib.rs index bd887400fe..79d6b61c84 100644 --- a/gui/src/rust/ide/ast/impl/src/lib.rs +++ b/gui/src/rust/ide/ast/impl/src/lib.rs @@ -92,7 +92,7 @@ pub struct WrongEnum {pub expected_con:String} // === Tree === // ============ -/// A tree structure where each node may store value of `K` and has arbitrary +/// A tree structure where each node may store value of `V` and has arbitrary /// number of children nodes, each marked with a single `K`. /// /// It is used to describe ambiguous macro match. diff --git a/gui/src/rust/ide/span-tree/src/action.rs b/gui/src/rust/ide/span-tree/src/action.rs index 02b550dc71..6407fee0f0 100644 --- a/gui/src/rust/ide/span-tree/src/action.rs +++ b/gui/src/rust/ide/span-tree/src/action.rs @@ -159,8 +159,8 @@ impl<'a> Implementation for node::Ref<'a> { fn erase_impl(&self) -> Option { match self.node.kind { - node::Kind::Argument{removable:true,..} | - node::Kind::Target {removable:true} => Some(Box::new(move |root| { + node::Kind::Argument{is_removable:true} | + node::Kind::Target {is_removable:true} => Some(Box::new(move |root| { let parent_crumb = &self.ast_crumbs[..self.ast_crumbs.len()-1]; let ast = root.get_traversing(parent_crumb)?; let new_ast = if let Some(mut infix) = ast::opr::Chain::try_new(ast) { @@ -233,46 +233,52 @@ mod test { let cases:&[Case] = & // Setting - [ Case{expr:"a + b" , span:0..5, action:Set , expected:"foo" } - , Case{expr:"a + b" , span:0..1, action:Set , expected:"foo + b" } - , Case{expr:"a + b" , span:4..5, action:Set , expected:"a + foo" } - , Case{expr:"a + b + c", span:0..1, action:Set , expected:"foo + b + c" } - , Case{expr:"a + b + c", span:4..5, action:Set , expected:"a + foo + c" } - , Case{expr:"a , b , c", span:0..1, action:Set , expected:"foo , b , c" } - , Case{expr:"a , b , c", span:4..5, action:Set , expected:"a , foo , c" } - , Case{expr:"a , b , c", span:8..9, action:Set , expected:"a , b , foo" } - , Case{expr:"f a b" , span:0..1, action:Set , expected:"foo a b" } - , Case{expr:"f a b" , span:2..3, action:Set , expected:"f foo b" } - , Case{expr:"f a b" , span:4..5, action:Set , expected:"f a foo" } - , Case{expr:"+ b" , span:0..0, action:Set , expected:"foo + b" } - , Case{expr:"+ b" , span:2..3, action:Set , expected:"+ foo" } - , Case{expr:"a +" , span:0..1, action:Set , expected:"foo +" } - , Case{expr:"a +" , span:3..3, action:Set , expected:"a + foo" } - , Case{expr:"+" , span:0..0, action:Set , expected:"foo +" } - , Case{expr:"+" , span:1..1, action:Set , expected:"+ foo" } - , Case{expr:"a + b" , span:0..0, action:Set , expected:"foo + a + b" } - , Case{expr:"a + b" , span:1..1, action:Set , expected:"a + foo + b" } - , Case{expr:"a + b" , span:5..5, action:Set , expected:"a + b + foo" } - , Case{expr:"+ b" , span:3..3, action:Set , expected:"+ b + foo" } - , Case{expr:"a + b + c", span:0..0, action:Set , expected:"foo + a + b + c"} - , Case{expr:"a + b + c", span:5..5, action:Set , expected:"a + b + foo + c"} - , Case{expr:"a , b , c", span:0..0, action:Set , expected:"foo , a , b , c"} - , Case{expr:"a , b , c", span:4..4, action:Set , expected:"a , foo , b , c"} - , Case{expr:"a , b , c", span:8..8, action:Set , expected:"a , b , foo , c"} - , Case{expr:"a , b , c", span:9..9, action:Set , expected:"a , b , c , foo"} - , Case{expr:", b" , span:3..3, action:Set , expected:", b , foo" } - , Case{expr:"f a b" , span:2..2, action:Set , expected:"f foo a b" } - , Case{expr:"f a b" , span:3..3, action:Set , expected:"f a foo b" } - , Case{expr:"f a b" , span:5..5, action:Set , expected:"f a b foo" } + [ Case{expr:"a + b" , span:0..5 , action:Set , expected:"foo" } + , Case{expr:"a + b" , span:0..1 , action:Set , expected:"foo + b" } + , Case{expr:"a + b" , span:4..5 , action:Set , expected:"a + foo" } + , Case{expr:"a + b + c" , span:0..1 , action:Set , expected:"foo + b + c" } + , Case{expr:"a + b + c" , span:4..5 , action:Set , expected:"a + foo + c" } + , Case{expr:"a , b , c" , span:0..1 , action:Set , expected:"foo , b , c" } + , Case{expr:"a , b , c" , span:4..5 , action:Set , expected:"a , foo , c" } + , Case{expr:"a , b , c" , span:8..9 , action:Set , expected:"a , b , foo" } + , Case{expr:"f a b" , span:0..1 , action:Set , expected:"foo a b" } + , Case{expr:"f a b" , span:2..3 , action:Set , expected:"f foo b" } + , Case{expr:"f a b" , span:4..5 , action:Set , expected:"f a foo" } + , Case{expr:"+ b" , span:0..0 , action:Set , expected:"foo + b" } + , Case{expr:"+ b" , span:2..3 , action:Set , expected:"+ foo" } + , Case{expr:"a +" , span:0..1 , action:Set , expected:"foo +" } + , Case{expr:"a +" , span:3..3 , action:Set , expected:"a + foo" } + , Case{expr:"+" , span:0..0 , action:Set , expected:"foo +" } + , Case{expr:"+" , span:1..1 , action:Set , expected:"+ foo" } + , Case{expr:"a + b" , span:0..0 , action:Set , expected:"foo + a + b" } + , Case{expr:"a + b" , span:1..1 , action:Set , expected:"a + foo + b" } + , Case{expr:"a + b" , span:5..5 , action:Set , expected:"a + b + foo" } + , Case{expr:"+ b" , span:3..3 , action:Set , expected:"+ b + foo" } + , Case{expr:"a + b + c" , span:0..0 , action:Set , expected:"foo + a + b + c"} + , Case{expr:"a + b + c" , span:5..5 , action:Set , expected:"a + b + foo + c"} + , Case{expr:"a , b , c" , span:0..0 , action:Set , expected:"foo , a , b , c"} + , Case{expr:"a , b , c" , span:4..4 , action:Set , expected:"a , foo , b , c"} + , Case{expr:"a , b , c" , span:8..8 , action:Set , expected:"a , b , foo , c"} + , Case{expr:"a , b , c" , span:9..9 , action:Set , expected:"a , b , c , foo"} + , Case{expr:", b" , span:3..3 , action:Set , expected:", b , foo" } + , Case{expr:"f a b" , span:2..2 , action:Set , expected:"f foo a b" } + , Case{expr:"f a b" , span:3..3 , action:Set , expected:"f a foo b" } + , Case{expr:"f a b" , span:5..5 , action:Set , expected:"f a b foo" } + , Case{expr:"if a then b", span:3..4 , action:Set , expected: "if foo then b" } + , Case{expr:"if a then b", span:10..11, action:Set , expected: "if a then foo" } + , Case{expr:"(a + b + c)", span:5..6 , action:Set , expected: "(a + foo + c)" } + , Case{expr:"(a + b + c" , span:5..6 , action:Set , expected: "(a + foo + c" } // Erasing - , Case{expr:"a + b + c", span:0..1, action:Erase, expected:"b + c" } - , Case{expr:"a + b + c", span:4..5, action:Erase, expected:"a + c" } - , Case{expr:"a + b + c", span:8..9, action:Erase, expected:"a + b" } - , Case{expr:"a , b , c", span:0..1, action:Erase, expected:"b , c" } - , Case{expr:"a , b , c", span:4..5, action:Erase, expected:"a , c" } - , Case{expr:"a , b , c", span:8..9, action:Erase, expected:"a , b" } - , Case{expr:"f a b" , span:2..3, action:Erase, expected:"f b" } - , Case{expr:"f a b" , span:4..5, action:Erase, expected:"f a" } + , Case{expr:"a + b + c" , span:0..1 , action:Erase, expected:"b + c" } + , Case{expr:"a + b + c" , span:4..5 , action:Erase, expected:"a + c" } + , Case{expr:"a + b + c" , span:8..9 , action:Erase, expected:"a + b" } + , Case{expr:"a , b , c" , span:0..1 , action:Erase, expected:"b , c" } + , Case{expr:"a , b , c" , span:4..5 , action:Erase, expected:"a , c" } + , Case{expr:"a , b , c" , span:8..9 , action:Erase, expected:"a , b" } + , Case{expr:"f a b" , span:2..3 , action:Erase, expected:"f b" } + , Case{expr:"f a b" , span:4..5 , action:Erase, expected:"f a" } + , Case{expr:"(a + b + c)", span:5..6 , action:Erase, expected: "(a + c)" } + , Case{expr:"(a + b + c" , span:5..6 , action:Erase, expected: "(a + c" } ]; let parser = Parser::new_or_panic(); for case in cases { case.run(&parser); } @@ -306,27 +312,32 @@ mod test { } } let cases:&[Case] = & - [ Case{expr:"abc" , span:0..3, expected: &[Set] } - , Case{expr:"a + b" , span:0..0, expected: &[Set] } - , Case{expr:"a + b" , span:0..1, expected: &[Set] } - , Case{expr:"a + b" , span:1..1, expected: &[Set] } - , Case{expr:"a + b" , span:2..3, expected: &[] } - , Case{expr:"a + b" , span:4..5, expected: &[Set] } - , Case{expr:"a + b" , span:5..5, expected: &[Set] } - , Case{expr:"a + b + c", span:0..0, expected: &[Set] } - , Case{expr:"a + b + c", span:0..1, expected: &[Set,Erase] } - , Case{expr:"a + b + c", span:1..1, expected: &[Set] } - , Case{expr:"a + b + c", span:4..5, expected: &[Set,Erase] } - , Case{expr:"a + b + c", span:5..5, expected: &[Set] } - , Case{expr:"a + b + c", span:8..9, expected: &[Set,Erase] } - , Case{expr:"a + b + c", span:9..9, expected: &[Set] } - , Case{expr:"f a b" , span:0..1, expected: &[Set] } - , Case{expr:"f a b" , span:2..2, expected: &[Set] } - , Case{expr:"f a b" , span:2..3, expected: &[Set,Erase] } - , Case{expr:"f a b" , span:3..3, expected: &[Set] } - , Case{expr:"f a b" , span:4..5, expected: &[Set,Erase] } - , Case{expr:"f a b" , span:5..5, expected: &[Set] } - , Case{expr:"f a" , span:2..3, expected: &[Set] } + [ Case{expr:"abc" , span:0..3 , expected: &[Set] } + , Case{expr:"a + b" , span:0..0 , expected: &[Set] } + , Case{expr:"a + b" , span:0..1 , expected: &[Set] } + , Case{expr:"a + b" , span:1..1 , expected: &[Set] } + , Case{expr:"a + b" , span:2..3 , expected: &[] } + , Case{expr:"a + b" , span:4..5 , expected: &[Set] } + , Case{expr:"a + b" , span:5..5 , expected: &[Set] } + , Case{expr:"a + b + c" , span:0..0 , expected: &[Set] } + , Case{expr:"a + b + c" , span:0..1 , expected: &[Set,Erase] } + , Case{expr:"a + b + c" , span:1..1 , expected: &[Set] } + , Case{expr:"a + b + c" , span:4..5 , expected: &[Set,Erase] } + , Case{expr:"a + b + c" , span:5..5 , expected: &[Set] } + , Case{expr:"a + b + c" , span:8..9 , expected: &[Set,Erase] } + , Case{expr:"a + b + c" , span:9..9 , expected: &[Set] } + , Case{expr:"f a b" , span:0..1 , expected: &[Set] } + , Case{expr:"f a b" , span:2..2 , expected: &[Set] } + , Case{expr:"f a b" , span:2..3 , expected: &[Set,Erase] } + , Case{expr:"f a b" , span:3..3 , expected: &[Set] } + , Case{expr:"f a b" , span:4..5 , expected: &[Set,Erase] } + , Case{expr:"f a b" , span:5..5 , expected: &[Set] } + , Case{expr:"f a" , span:2..3 , expected: &[Set] } + , Case{expr:"if a then b", span:3..4 , expected: &[Set] } + , Case{expr:"if a then b", span:10..11, expected: &[Set] } + , Case{expr:"(a + b + c)", span:5..6 , expected: &[Set,Erase] } + , Case{expr:"(a" , span:1..2 , expected: &[Set] } + , Case{expr:"(a + b + c" , span:5..6 , expected: &[Set,Erase] } ]; let parser = Parser::new_or_panic(); for case in cases { case.run(&parser); } diff --git a/gui/src/rust/ide/span-tree/src/generate.rs b/gui/src/rust/ide/span-tree/src/generate.rs index 3276d067b2..3529058142 100644 --- a/gui/src/rust/ide/span-tree/src/generate.rs +++ b/gui/src/rust/ide/span-tree/src/generate.rs @@ -1,4 +1,5 @@ //! A module containing code related to SpanTree generation. +pub mod macros; use crate::prelude::*; @@ -8,6 +9,8 @@ use crate::Node; use crate::SpanTree; use ast::Ast; +use ast::MacroMatchSegment; +use ast::MacroAmbiguousSegment; use ast::assoc::Assoc; use ast::crumbs::Located; use ast::HasLength; @@ -15,6 +18,7 @@ use ast::opr::GeneralizedInfix; use data::text::Size; + // ============= // === Trait === // ============= @@ -34,6 +38,7 @@ pub trait SpanTreeGenerator { } + // ================= // === Utilities === // ================= @@ -101,13 +106,19 @@ impl SpanTreeGenerator for Ast { infix.flatten().generate_node(kind) } else { match self.shape() { - ast::Shape::Prefix {..} => + ast::Shape::Prefix(_) => ast::prefix::Chain::try_new(self).unwrap().generate_node(kind), - // TODO[a] add other shapes, e.g. macros - _ => Ok(Node {kind, - size : Size::new(self.len()), - children : default(), - }), + // Lambdas should fall in _ case, because we don't want to create subports for + // them + ast::Shape::Match(ast) if ast::macros::as_lambda_match(self).is_none() => + ast.generate_node(kind), + ast::Shape::Ambiguous(ast) => + ast.generate_node(kind), + _ => { + let size = Size::new(self.len()); + let children = default(); + Ok(Node {kind,size,children}) + }, } } } @@ -120,10 +131,12 @@ impl SpanTreeGenerator for ast::opr::Chain { fn generate_node(&self, kind:node::Kind) -> FallibleResult { // Removing operands is possible only when chain has at least 3 of them // (target and two arguments). - let removable = self.args.len() >= 2; - let node_and_offset = match &self.target { - Some(target) => - target.arg.generate_node(node::Kind::Target {removable}).map(|n| (n,target.offset)), + let is_removable = self.args.len() >= 2; + let node_and_offset:FallibleResult<(Node,usize)> = match &self.target { + Some(target) => { + let node = target.arg.generate_node(node::Kind::Target {is_removable})?; + Ok((node,target.offset)) + }, None => Ok((Node::new_empty(InsertType::BeforeTarget),0)), }; @@ -151,7 +164,8 @@ impl SpanTreeGenerator for ast::opr::Chain { let arg_crumbs = elem.crumb_to_operand(has_left); let arg_ast = Located::new(arg_crumbs,operand.arg.clone_ref()); gen.spacing(operand.offset); - gen.generate_ast_node(arg_ast,node::Kind::Argument {removable})?; + + gen.generate_ast_node(arg_ast,node::Kind::Argument {is_removable})?; } gen.generate_empty_node(InsertType::Append); @@ -176,14 +190,14 @@ impl SpanTreeGenerator for ast::prefix::Chain { fn generate_node(&self, kind:node::Kind) -> FallibleResult { use ast::crumbs::PrefixCrumb::*; // Removing arguments is possible if there at least two of them - let removable = self.args.len() >= 2; - let node = self.func.generate_node(node::Kind::Operation); + let is_removable = self.args.len() >= 2; + let node = self.func.generate_node(node::Kind::Operation); self.args.iter().enumerate().fold(node, |node,(i,arg)| { let node = node?; let is_first = i == 0; let is_last = i + 1 == self.args.len(); - let arg_kind = if is_first { node::Kind::Target {removable} } - else { node::Kind::Argument {removable} }; + let arg_kind = if is_first { node::Kind::Target {is_removable} } + else { node::Kind::Argument {is_removable} }; let mut gen = ChildGenerator::default(); gen.add_node(vec![Func.into()],node); @@ -203,6 +217,82 @@ impl SpanTreeGenerator for ast::prefix::Chain { } +// === Match === + +impl SpanTreeGenerator for ast::Match { + fn generate_node(&self, kind:node::Kind) -> FallibleResult { + let is_removable = false; + let children_kind = node::Kind::Argument {is_removable}; + let mut gen = ChildGenerator::default(); + if let Some(pat) = &self.pfx { + for macros::AstInPattern {ast,crumbs} in macros::all_ast_nodes_in_pattern(&pat) { + let ast_crumb = ast::crumbs::MatchCrumb::Pfx {val:crumbs}; + let located_ast = Located::new(ast_crumb,ast.wrapped); + gen.generate_ast_node(located_ast,children_kind)?; + gen.spacing(ast.off); + } + } + let first_segment_index = 0; + generate_children_from_segment(&mut gen,first_segment_index,&self.segs.head)?; + for (index,segment) in self.segs.tail.iter().enumerate() { + gen.spacing(segment.off); + generate_children_from_segment(&mut gen,index+1,&segment.wrapped)?; + } + Ok(Node {kind, + size : gen.current_offset, + children : gen.children, + }) + } +} + +fn generate_children_from_segment +(gen:&mut ChildGenerator, index:usize, segment:&MacroMatchSegment) -> FallibleResult<()> { + let is_removable = false; + let children_kind = node::Kind::Argument {is_removable}; + gen.spacing(segment.head.len()); + for macros::AstInPattern {ast,crumbs} in macros::all_ast_nodes_in_pattern(&segment.body) { + gen.spacing(ast.off); + let segment_crumb = ast::crumbs::SegmentMatchCrumb::Body {val:crumbs}; + let ast_crumb = ast::crumbs::MatchCrumb::Segs{val:segment_crumb, index}; + let located_ast = Located::new(ast_crumb,ast.wrapped); + gen.generate_ast_node(located_ast,children_kind)?; + } + Ok(()) +} + + +// === Ambiguous == + +impl SpanTreeGenerator for ast::Ambiguous { + fn generate_node(&self, kind:node::Kind) -> FallibleResult { + let mut gen = ChildGenerator::default(); + let first_segment_index = 0; + generate_children_from_abiguous_segment(&mut gen,first_segment_index,&self.segs.head)?; + for (index,segment) in self.segs.tail.iter().enumerate() { + gen.spacing(segment.off); + generate_children_from_abiguous_segment(&mut gen, index+1, &segment.wrapped)?; + } + Ok(Node{kind, + size : gen.current_offset, + children : gen.children, + }) + } +} + +fn generate_children_from_abiguous_segment +(gen:&mut ChildGenerator, index:usize, segment:&MacroAmbiguousSegment) -> FallibleResult<()> { + let is_removable = false; + let children_kind = node::Kind::Argument {is_removable}; + gen.spacing(segment.head.len()); + if let Some(sast) = &segment.body { + gen.spacing(sast.off); + let field = ast::crumbs::AmbiguousSegmentCrumb::Body; + let located_ast = Located::new(ast::crumbs::AmbiguousCrumb{index,field}, sast.clone_ref()); + gen.generate_ast_node(located_ast,children_kind)?; + } + Ok(()) +} + // ============ // === Test === @@ -216,7 +306,10 @@ mod test { use crate::node::Kind::*; use crate::node::InsertType::*; + use ast::crumbs::AmbiguousCrumb; + use ast::crumbs::AmbiguousSegmentCrumb; use ast::crumbs::InfixCrumb; + use ast::crumbs::PatternMatchCrumb; use ast::crumbs::PrefixCrumb; use ast::crumbs::SectionLeftCrumb; use ast::crumbs::SectionRightCrumb; @@ -229,29 +322,29 @@ mod test { #[wasm_bindgen_test] fn generating_span_tree() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line("2 + foo bar - 3").unwrap(); - let tree = ast.generate_tree().unwrap(); - let removable = false; + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("2 + foo bar - 3").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = false; let expected = TreeBuilder::new(15) .add_empty_child(0,BeforeTarget) - .add_child(0,11,Target{removable},vec![InfixCrumb::LeftOperand]) + .add_child(0,11,Target{is_removable},InfixCrumb::LeftOperand) .add_empty_child(0,BeforeTarget) - .add_leaf (0,1,Target{removable},vec![InfixCrumb::LeftOperand]) + .add_leaf (0,1,Target{is_removable},InfixCrumb::LeftOperand) .add_empty_child(1,AfterTarget) - .add_leaf (2,1,Operation,vec![InfixCrumb::Operator]) - .add_child(4,7,Argument{removable} ,vec![InfixCrumb::RightOperand]) - .add_leaf(0,3,Operation,vec![PrefixCrumb::Func]) + .add_leaf (2,1,Operation,InfixCrumb::Operator) + .add_child(4,7,Argument{is_removable} ,InfixCrumb::RightOperand) + .add_leaf(0,3,Operation,PrefixCrumb::Func) .add_empty_child(4,BeforeTarget) - .add_leaf(4,3,Target{removable},vec![PrefixCrumb::Arg]) + .add_leaf(4,3,Target{is_removable},PrefixCrumb::Arg) .add_empty_child(7,Append) .done() .add_empty_child(11,Append) .done() .add_empty_child(11,AfterTarget) - .add_leaf(12,1,Operation,vec![InfixCrumb::Operator]) - .add_leaf(14,1,Argument{removable},vec![InfixCrumb::RightOperand]) + .add_leaf(12,1,Operation,InfixCrumb::Operator) + .add_leaf(14,1,Argument{is_removable},InfixCrumb::RightOperand) .add_empty_child(15,Append) .build(); @@ -260,40 +353,40 @@ mod test { #[wasm_bindgen_test] fn generate_span_tree_with_chains() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line("2 + 3 + foo bar baz 13 + 5").unwrap(); - let tree = ast.generate_tree().unwrap(); - let removable = true; + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("2 + 3 + foo bar baz 13 + 5").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = true; let expected = TreeBuilder::new(26) - .add_child(0,22,Chained,vec![InfixCrumb::LeftOperand]) - .add_child(0,5,Chained,vec![InfixCrumb::LeftOperand]) + .add_child(0,22,Chained,InfixCrumb::LeftOperand) + .add_child(0,5,Chained,InfixCrumb::LeftOperand) .add_empty_child(0,BeforeTarget) - .add_leaf(0,1,Target{removable},vec![InfixCrumb::LeftOperand]) + .add_leaf(0,1,Target{is_removable},InfixCrumb::LeftOperand) .add_empty_child(1,AfterTarget) - .add_leaf(2,1,Operation,vec![InfixCrumb::Operator]) - .add_leaf(4,1,Argument{removable},vec![InfixCrumb::RightOperand]) + .add_leaf(2,1,Operation,InfixCrumb::Operator) + .add_leaf(4,1,Argument{is_removable},InfixCrumb::RightOperand) .add_empty_child(5,Append) .done() - .add_leaf (6,1 ,Operation,vec![InfixCrumb::Operator]) - .add_child(8,14,Argument{removable},vec![InfixCrumb::RightOperand]) - .add_child(0,11,Chained,vec![PrefixCrumb::Func]) - .add_child(0,7,Chained,vec![PrefixCrumb::Func]) - .add_leaf(0,3,Operation,vec![PrefixCrumb::Func]) + .add_leaf (6,1 ,Operation,InfixCrumb::Operator) + .add_child(8,14,Argument{is_removable},InfixCrumb::RightOperand) + .add_child(0,11,Chained,PrefixCrumb::Func) + .add_child(0,7,Chained,PrefixCrumb::Func) + .add_leaf(0,3,Operation,PrefixCrumb::Func) .add_empty_child(4,BeforeTarget) - .add_leaf(4,3,Target{removable},vec![PrefixCrumb::Arg]) + .add_leaf(4,3,Target{is_removable},PrefixCrumb::Arg) .add_empty_child(7,Append) .done() - .add_leaf(8,3,Argument{removable},vec![PrefixCrumb::Arg]) + .add_leaf(8,3,Argument{is_removable},PrefixCrumb::Arg) .add_empty_child(11,Append) .done() - .add_leaf(12,2,Argument{removable},vec![PrefixCrumb::Arg]) + .add_leaf(12,2,Argument{is_removable},PrefixCrumb::Arg) .add_empty_child(14,Append) .done() .add_empty_child(22,Append) .done() - .add_leaf(23,1,Operation,vec![InfixCrumb::Operator]) - .add_leaf(25,1,Argument{removable},vec![InfixCrumb::RightOperand]) + .add_leaf(23,1,Operation,InfixCrumb::Operator) + .add_leaf(25,1,Argument{is_removable},InfixCrumb::RightOperand) .add_empty_child(26,Append) .build(); @@ -302,21 +395,21 @@ mod test { #[wasm_bindgen_test] fn generating_span_tree_from_right_assoc_operator() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line("1,2,3").unwrap(); - let tree = ast.generate_tree().unwrap(); - let removable = true; + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("1,2,3").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = true; let expected = TreeBuilder::new(5) .add_empty_child(0,Append) - .add_leaf (0,1,Argument{removable},vec![InfixCrumb::LeftOperand]) - .add_leaf (1,1,Operation,vec![InfixCrumb::Operator]) - .add_child(2,3,Chained ,vec![InfixCrumb::RightOperand]) + .add_leaf (0,1,Argument{is_removable},InfixCrumb::LeftOperand) + .add_leaf (1,1,Operation,InfixCrumb::Operator) + .add_child(2,3,Chained ,InfixCrumb::RightOperand) .add_empty_child(0,Append) - .add_leaf(0,1,Argument{removable},vec![InfixCrumb::LeftOperand]) - .add_leaf(1,1,Operation,vec![InfixCrumb::Operator]) + .add_leaf(0,1,Argument{is_removable},InfixCrumb::LeftOperand) + .add_leaf(1,1,Operation,InfixCrumb::Operator) .add_empty_child(2,AfterTarget) - .add_leaf(2,1,Target{removable},vec![InfixCrumb::RightOperand]) + .add_leaf(2,1,Target{is_removable},InfixCrumb::RightOperand) .add_empty_child(3,BeforeTarget) .done() .build(); @@ -329,31 +422,31 @@ mod test { let parser = Parser::new_or_panic(); // The star makes `SectionSides` ast being one of the parameters of + chain. First + makes // SectionRight, and last + makes SectionLeft. - let ast = parser.parse_line("+ * + + 2 +").unwrap(); - let tree = ast.generate_tree().unwrap(); - let removable = true; + let ast = parser.parse_line("+ * + + 2 +").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = true; let expected = TreeBuilder::new(11) - .add_child(0,9,Chained,vec![SectionLeftCrumb::Arg]) - .add_child(0,5,Chained,vec![InfixCrumb::LeftOperand]) - .add_child(0,3,Chained,vec![SectionLeftCrumb::Arg]) + .add_child(0,9,Chained,SectionLeftCrumb::Arg) + .add_child(0,5,Chained,InfixCrumb::LeftOperand) + .add_child(0,3,Chained,SectionLeftCrumb::Arg) .add_empty_child(0,BeforeTarget) - .add_leaf (0,1,Operation,vec![SectionRightCrumb::Opr]) - .add_child(2,1,Argument{removable},vec![SectionRightCrumb::Arg]) + .add_leaf (0,1,Operation,SectionRightCrumb::Opr) + .add_child(2,1,Argument{is_removable},SectionRightCrumb::Arg) .add_empty_child(0,BeforeTarget) - .add_leaf(0,1,Operation,vec![SectionSidesCrumb]) + .add_leaf(0,1,Operation,SectionSidesCrumb) .add_empty_child(1,Append) .done() .add_empty_child(3,Append) .done() - .add_leaf(4,1,Operation,vec![SectionLeftCrumb::Opr]) + .add_leaf(4,1,Operation,SectionLeftCrumb::Opr) .add_empty_child(5,Append) .done() - .add_leaf(6,1,Operation,vec![InfixCrumb::Operator]) - .add_leaf(8,1,Argument{removable},vec![InfixCrumb::RightOperand]) + .add_leaf(6,1,Operation,InfixCrumb::Operator) + .add_leaf(8,1,Argument{is_removable},InfixCrumb::RightOperand) .add_empty_child(9,Append) .done() - .add_leaf(10,1,Operation,vec![SectionLeftCrumb::Opr]) + .add_leaf(10,1,Operation,SectionLeftCrumb::Opr) .add_empty_child(11,Append) .build(); @@ -362,22 +455,93 @@ mod test { #[wasm_bindgen_test] fn generating_span_tree_from_right_assoc_section() { - let parser = Parser::new_or_panic(); - let ast = parser.parse_line(",2,").unwrap(); - let tree = ast.generate_tree().unwrap(); - let removable = true; + let parser = Parser::new_or_panic(); + let ast = parser.parse_line(",2,").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = true; let expected = TreeBuilder::new(3) .add_empty_child(0,Append) - .add_leaf (0,1,Operation,vec![SectionRightCrumb::Opr]) - .add_child(1,2,Chained ,vec![SectionRightCrumb::Arg]) + .add_leaf (0,1,Operation,SectionRightCrumb::Opr) + .add_child(1,2,Chained ,SectionRightCrumb::Arg) .add_empty_child(0,Append) - .add_leaf(0,1,Argument{removable},vec![SectionLeftCrumb::Arg]) - .add_leaf(1,1,Operation,vec![SectionLeftCrumb::Opr]) + .add_leaf(0,1,Argument{is_removable},SectionLeftCrumb::Arg) + .add_leaf(1,1,Operation,SectionLeftCrumb::Opr) .add_empty_child(2,BeforeTarget) .done() .build(); assert_eq!(expected,tree); } + + #[wasm_bindgen_test] + fn generating_span_tree_from_matched_macros() { + use PatternMatchCrumb::*; + + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("if foo then (a + b) x else ()").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = false; + + let if_then_else_cr = vec![Seq { right: false }, Or, Build]; + let parens_cr = vec![Seq { right: false }, Or, Or, Build]; + let segment_body_crumbs = |index:usize, pattern_crumb:&Vec| { + let val = ast::crumbs::SegmentMatchCrumb::Body {val:pattern_crumb.clone()}; + ast::crumbs::MatchCrumb::Segs {val,index} + }; + + let expected = TreeBuilder::new(29) + .add_leaf(3,3,Argument {is_removable},segment_body_crumbs(0,&if_then_else_cr)) + .add_child(12,9,Argument {is_removable},segment_body_crumbs(1,&if_then_else_cr)) + .add_child(0,7,Operation,PrefixCrumb::Func) + .add_child(1,5,Argument {is_removable},segment_body_crumbs(0,&parens_cr)) + .add_empty_child(0,BeforeTarget) + .add_leaf(0,1,Target {is_removable},InfixCrumb::LeftOperand) + .add_empty_child(1,AfterTarget) + .add_leaf(2,1,Operation,InfixCrumb::Operator) + .add_leaf(4,1,Argument {is_removable},InfixCrumb::RightOperand) + .add_empty_child(5,Append) + .done() + .done() + .add_empty_child(8,BeforeTarget) + .add_leaf(8,1,Target {is_removable},PrefixCrumb::Arg) + .add_empty_child(9,Append) + .done() + .add_leaf(27,2,Argument {is_removable},segment_body_crumbs(2,&if_then_else_cr)) + .build(); + + assert_eq!(expected,tree); + } + + #[wasm_bindgen_test] + fn generating_span_tree_from_ambiguous_macros() { + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("(4").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = false; + let crumb = AmbiguousCrumb{index:0, field:AmbiguousSegmentCrumb::Body}; + + let expected = TreeBuilder::new(2) + .add_leaf(1,1,Argument {is_removable},crumb) + .build(); + + assert_eq!(expected,tree); + } + + #[wasm_bindgen_test] + fn generating_span_tree_for_lambda() { + let parser = Parser::new_or_panic(); + let ast = parser.parse_line("foo a-> b + c").unwrap(); + let tree = ast.generate_tree().unwrap(); + let is_removable = false; + + let expected = TreeBuilder::new(13) + .add_leaf(0,3,Operation,PrefixCrumb::Func) + .add_empty_child(4,BeforeTarget) + .add_leaf(4,9,Target{is_removable},PrefixCrumb::Arg) + .add_empty_child(13,Append) + .build(); + + assert_eq!(expected,tree); + } } diff --git a/gui/src/rust/ide/span-tree/src/generate/macros.rs b/gui/src/rust/ide/span-tree/src/generate/macros.rs new file mode 100644 index 0000000000..d79437048c --- /dev/null +++ b/gui/src/rust/ide/span-tree/src/generate/macros.rs @@ -0,0 +1,142 @@ +//! A module with utilities for generating SpanTree from macros (Match and Ambiguous). + +// TODO[ao] Duplicated with `pattern_subcrumbs` function in `ast::crumbs`, but adds information +// about spacing. All the 'crumblike' utilities should be merged to one solution + +use crate::prelude::*; + +use ast::Ast; +use ast::MacroPatternMatch; +use ast::Shifted; +use ast::crumbs::PatternMatchCrumb; + + + +// ====================== +// === LocatedPattern === +// ====================== + +/// A fragment of MacroPatternMatch localized by PatternMatchCrumbs. +#[allow(missing_docs)] +#[derive(Debug)] +pub struct LocatedPattern<'a> { + pub pattern : &'a MacroPatternMatch>, + pub crumbs : Vec, +} + + + +// ================== +// === PatternDfs === +// ================== + +/// A iterator over all nodes in MacroPatternMatch tree, traversing it with DFS algorithm. +struct PatternDfs<'a> { + /// The FILO queue of nodes to visit. + to_visit: Vec> +} + +impl<'a> Iterator for PatternDfs<'a> { + type Item = LocatedPattern<'a>; + + fn next(&mut self) -> Option { + let to_return = self.to_visit.pop(); + if let Some(pattern) = &to_return { + self.push_children_to_visit(pattern); + } + to_return + } +} + +impl<'a> PatternDfs<'a> { + /// Create iterator which start from `root` node. + pub fn new(root:&'a MacroPatternMatch>) -> Self { + let first_to_visit = LocatedPattern { + pattern : root, + crumbs : vec![], + }; + PatternDfs { + to_visit: vec![first_to_visit], + } + } + + /// Obtain all children of `pattern` and push them to `to_visit` queue. + fn push_children_to_visit(&mut self, pattern:&LocatedPattern<'a>) { + use ast::MacroPatternMatchRaw::*; + match pattern.pattern.deref() { + Except(pat) => self.push_child_to_visit(&pattern,&pat.elem,PatternMatchCrumb::Except), + Tag(pat) => self.push_child_to_visit(&pattern,&pat.elem,PatternMatchCrumb::Tag), + Cls(pat) => self.push_child_to_visit(&pattern,&pat.elem,PatternMatchCrumb::Cls), + Or(pat) => self.push_child_to_visit(&pattern,&pat.elem,PatternMatchCrumb::Or), + Seq(pat) => { + let (left_elem,right_elem) = &pat.elem; + self.push_child_to_visit(&pattern,left_elem ,PatternMatchCrumb::Seq{right:false}); + self.push_child_to_visit(&pattern,right_elem,PatternMatchCrumb::Seq{right:true}); + }, + Many(pat) => { + for (index,elem) in pat.elem.iter().enumerate().rev() { + self.push_child_to_visit(&pattern,elem,PatternMatchCrumb::Many {index}); + } + } + // Other patterns does not have children. + _ => {} + } + } + + fn push_child_to_visit + ( &mut self + , pattern : &LocatedPattern<'a> + , child : &'a MacroPatternMatch> + , crumb : PatternMatchCrumb + ) { + let loc_pattern = LocatedPattern { + pattern : child, + crumbs : pattern.crumbs.iter().cloned().chain(std::iter::once(crumb)).collect() + }; + self.to_visit.push(loc_pattern); + } +} + + +// ========================================== +// === Retrieving AST Nodes From Patterns === +// ========================================== + +/// An AST node being inside a Match node +#[allow(missing_docs)] +#[derive(Debug)] +pub struct AstInPattern { + pub ast : Shifted, + pub crumbs : Vec, +} + +/// Helper function that returns all AST nodes being on leaves of MacroPatternMatch. +pub fn all_ast_nodes_in_pattern<'a> +(pattern:&'a MacroPatternMatch>) -> impl Iterator + 'a { + use ast::MacroPatternMatchRaw::*; + + PatternDfs::new(pattern).filter_map(|pattern| { + let opt_ast_and_crumb = match pattern.pattern.deref() { + Build(pat) => Some((&pat.elem,PatternMatchCrumb::Build )), + Err(pat) => Some((&pat.elem,PatternMatchCrumb::Err )), + Tok(pat) => Some((&pat.elem,PatternMatchCrumb::Tok )), + Blank(pat) => Some((&pat.elem,PatternMatchCrumb::Blank )), + Var(pat) => Some((&pat.elem,PatternMatchCrumb::Var )), + Cons(pat) => Some((&pat.elem,PatternMatchCrumb::Cons )), + Opr(pat) => Some((&pat.elem,PatternMatchCrumb::Opr )), + Mod(pat) => Some((&pat.elem,PatternMatchCrumb::Mod )), + Num(pat) => Some((&pat.elem,PatternMatchCrumb::Num )), + Text(pat) => Some((&pat.elem,PatternMatchCrumb::Text )), + Block(pat) => Some((&pat.elem,PatternMatchCrumb::Block )), + Macro(pat) => Some((&pat.elem,PatternMatchCrumb::Macro )), + Invalid(pat) => Some((&pat.elem,PatternMatchCrumb::Invalid)), + _ => None, + }; + opt_ast_and_crumb.map(|(ast,crumb)| { + AstInPattern { + ast : ast.clone(), + crumbs : pattern.crumbs.into_iter().chain(std::iter::once(crumb)).collect() + } + }) + }) +} diff --git a/gui/src/rust/ide/span-tree/src/iter.rs b/gui/src/rust/ide/span-tree/src/iter.rs index 14f627118a..856cb1b78b 100644 --- a/gui/src/rust/ide/span-tree/src/iter.rs +++ b/gui/src/rust/ide/span-tree/src/iter.rs @@ -126,24 +126,24 @@ mod tests { // /| / | \ // gg-children: ()() ()() () - let removable = false; + let is_removable = false; let tree = TreeBuilder::new(14) .add_child(0,10,Chained,vec![LeftOperand]) - .add_leaf (0,3,Target{removable},vec![LeftOperand]) + .add_leaf (0,3,Target{is_removable},vec![LeftOperand]) .add_leaf (4,1,Operation,vec![Operator]) - .add_child(6,3,Argument{removable},vec![RightOperand]) + .add_child(6,3,Argument{is_removable},vec![RightOperand]) .add_leaf(0,1,Operation,vec![Func]) - .add_leaf(2,1,Target{removable},vec![Arg]) + .add_leaf(2,1,Target{is_removable},vec![Arg]) .done() .done() .add_leaf (11,1,Operation,vec![Operator]) .add_child(13,1,Chained ,vec![RightOperand]) - .add_leaf (0,3,Target{removable},vec![LeftOperand]) + .add_leaf (0,3,Target{is_removable},vec![LeftOperand]) .add_leaf (4,1,Operation,vec![Operator]) .add_child(6,5,Chained,vec![RightOperand]) - .add_leaf(0,1,Target{removable},vec![LeftOperand]) + .add_leaf(0,1,Target{is_removable},vec![LeftOperand]) .add_leaf(2,1,Operation,vec![Operator]) - .add_leaf(4,1,Argument{removable},vec![RightOperand]) + .add_leaf(4,1,Argument{is_removable},vec![RightOperand]) .done() .done() .build(); diff --git a/gui/src/rust/ide/span-tree/src/node.rs b/gui/src/rust/ide/span-tree/src/node.rs index 293a1b4daf..83b8ce42a6 100644 --- a/gui/src/rust/ide/span-tree/src/node.rs +++ b/gui/src/rust/ide/span-tree/src/node.rs @@ -27,12 +27,12 @@ pub enum Kind { /// A node being a target (or "self") parameter of parent Infix, Section or Prefix. Target { /// Indicates if this node can be erased from SpanTree. - removable:bool + is_removable:bool }, /// A node being a normal (not target) parameter of parent Infix, Section or Prefix. Argument { /// Indicates if this node can be erased from SpanTree. - removable:bool + is_removable:bool }, /// A node being a placeholder for inserting new child to Prefix or Operator chain. It should /// have assigned span of length 0 and should not have any child. @@ -226,14 +226,14 @@ mod test { fn node_lookup() { use ast::crumbs::InfixCrumb::*; - let removable = false; + let is_removable = false; let tree = TreeBuilder::new(7) - .add_leaf (0,1,Target{removable},vec![LeftOperand]) + .add_leaf (0,1,Target{is_removable},vec![LeftOperand]) .add_leaf (1,1,Operation,vec![Operator]) - .add_child(2,5,Argument{removable},vec![RightOperand]) - .add_leaf(0,2,Target{removable},vec![LeftOperand]) + .add_child(2,5,Argument{is_removable},vec![RightOperand]) + .add_leaf(0,2,Target{is_removable},vec![LeftOperand]) .add_leaf(3,1,Operation,vec![Operator]) - .add_leaf(4,1,Argument{removable},vec![RightOperand]) + .add_leaf(4,1,Argument{is_removable},vec![RightOperand]) .done() .build(); @@ -285,14 +285,14 @@ mod test { use ast::crumbs::InfixCrumb::*; use ast::crumbs::PrefixCrumb::*; - let removable = false; + let is_removable = false; let tree = TreeBuilder::new(7) - .add_leaf (0,1,Target{removable},vec![LeftOperand]) + .add_leaf (0,1,Target{is_removable},vec![LeftOperand]) .add_empty_child(1,InsertType::AfterTarget) .add_leaf (1,1,Operation,vec![Operator]) - .add_child(2,5,Argument{removable},vec![RightOperand]) + .add_child(2,5,Argument{is_removable},vec![RightOperand]) .add_leaf(0,3,Operation,vec![Func]) - .add_leaf(3,1,Target{removable},vec![Arg]) + .add_leaf(3,1,Target{is_removable},vec![Arg]) .done() .build();