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: aeff31f2d8
This commit is contained in:
Adam Obuchowicz 2020-04-30 12:23:19 +02:00 committed by GitHub
parent bc2551daf6
commit ef5cb279a3
6 changed files with 475 additions and 158 deletions

View File

@ -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.

View File

@ -159,8 +159,8 @@ impl<'a> Implementation for node::Ref<'a> {
fn erase_impl(&self) -> Option<EraseOperation> {
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); }

View File

@ -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<Node> {
// 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<Node> {
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<Ast> {
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
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<Ast>) -> 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<Ast> {
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
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<Ast>) -> 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<PatternMatchCrumb>| {
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);
}
}

View File

@ -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<Shifted<Ast>>,
pub crumbs : Vec<PatternMatchCrumb>,
}
// ==================
// === 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<LocatedPattern<'a>>
}
impl<'a> Iterator for PatternDfs<'a> {
type Item = LocatedPattern<'a>;
fn next(&mut self) -> Option<Self::Item> {
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<Shifted<Ast>>) -> 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<Shifted<Ast>>
, 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<Ast>,
pub crumbs : Vec<PatternMatchCrumb>,
}
/// Helper function that returns all AST nodes being on leaves of MacroPatternMatch.
pub fn all_ast_nodes_in_pattern<'a>
(pattern:&'a MacroPatternMatch<Shifted<Ast>>) -> impl Iterator<Item=AstInPattern> + '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()
}
})
})
}

View File

@ -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();

View File

@ -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();