mirror of
https://github.com/enso-org/enso.git
synced 2024-12-30 21:44:07 +03:00
Expression id in span tree (https://github.com/enso-org/ide/pull/599)
The optional Expression Id was added to SpanTree, to allow reading type information by views.
Original commit: b85eeba5aa
This commit is contained in:
parent
cb6a16d402
commit
87326a2f3b
@ -127,7 +127,7 @@ commands.build.rust = async function(argv) {
|
||||
|
||||
console.log('Checking the resulting WASM size.')
|
||||
let stats = fss.statSync(paths.dist.wasm.mainOptGz)
|
||||
let limit = 3.32
|
||||
let limit = 3.33
|
||||
let size = Math.round(100 * stats.size / 1024 / 1024) / 100
|
||||
if (size > limit) {
|
||||
throw(`Output file size exceeds the limit (${size}MB > ${limit}MB).`)
|
||||
|
@ -75,14 +75,17 @@ impl IdMap {
|
||||
IdMap {vec}
|
||||
}
|
||||
/// Assigns Span to given ID.
|
||||
pub fn insert(&mut self, span:Span, id:Id) {
|
||||
self.vec.push((span,id));
|
||||
pub fn insert(&mut self, span:impl Into<Span>, id:Id) {
|
||||
self.vec.push((span.into(),id));
|
||||
}
|
||||
/// Generate random Uuid for span.
|
||||
pub fn generate(&mut self, span:impl Into<Span>) {
|
||||
self.vec.push((span.into(),Uuid::new_v4()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::Id;
|
||||
use crate::Infix;
|
||||
use crate::SectionLeft;
|
||||
use crate::SectionRight;
|
||||
@ -129,6 +130,8 @@ pub struct GeneralizedInfix {
|
||||
pub opr : Operator,
|
||||
/// Right operand, if present.
|
||||
pub right : Operand,
|
||||
/// Infix id.
|
||||
pub id : Option<Id>,
|
||||
}
|
||||
|
||||
/// A structure used for GeneralizedInfix construction which marks operands as _target_ and
|
||||
@ -144,23 +147,28 @@ impl GeneralizedInfix {
|
||||
/// Tries interpret given AST node as GeneralizedInfix. Returns None, if Ast is not any kind of
|
||||
/// application on infix operator.
|
||||
pub fn try_new(ast:&Ast) -> Option<GeneralizedInfix> {
|
||||
let id = ast.id;
|
||||
match ast.shape().clone() {
|
||||
Shape::Infix(infix) => Some(GeneralizedInfix{
|
||||
id,
|
||||
left : make_operand (infix.larg,infix.loff),
|
||||
opr : make_operator(&infix.opr)?,
|
||||
right : make_operand (infix.rarg,infix.roff),
|
||||
}),
|
||||
Shape::SectionLeft(left) => Some(GeneralizedInfix{
|
||||
id,
|
||||
left : make_operand (left.arg,left.off),
|
||||
opr : make_operator(&left.opr)?,
|
||||
right : None,
|
||||
}),
|
||||
Shape::SectionRight(right) => Some(GeneralizedInfix{
|
||||
id,
|
||||
left : None,
|
||||
opr : make_operator(&right.opr)?,
|
||||
right : make_operand (right.arg,right.off),
|
||||
}),
|
||||
Shape::SectionSides(sides) => Some(GeneralizedInfix{
|
||||
id,
|
||||
left : None,
|
||||
opr : make_operator(&sides.opr)?,
|
||||
right : None,
|
||||
@ -170,13 +178,13 @@ impl GeneralizedInfix {
|
||||
}
|
||||
|
||||
/// Constructor with operands marked as target and argument.
|
||||
pub fn new_from_operands(operands:MarkedOperands, opr:Operator) -> Self {
|
||||
pub fn new_from_operands(operands:MarkedOperands, opr:Operator, id:Option<Id>) -> Self {
|
||||
match assoc(&opr) {
|
||||
Assoc::Left => GeneralizedInfix {opr,
|
||||
Assoc::Left => GeneralizedInfix {opr,id,
|
||||
left : operands.target,
|
||||
right : operands.argument,
|
||||
},
|
||||
Assoc::Right => GeneralizedInfix {opr,
|
||||
Assoc::Right => GeneralizedInfix {opr,id,
|
||||
left : operands.argument,
|
||||
right : operands.target,
|
||||
},
|
||||
@ -185,7 +193,7 @@ impl GeneralizedInfix {
|
||||
|
||||
/// Convert to AST node.
|
||||
pub fn into_ast(self) -> Ast {
|
||||
match (self.left,self.right) {
|
||||
let ast:Ast = match (self.left,self.right) {
|
||||
(Some(left),Some(right)) => Infix{
|
||||
larg : left.arg,
|
||||
loff : left.offset,
|
||||
@ -206,6 +214,11 @@ impl GeneralizedInfix {
|
||||
(None,None) => SectionSides {
|
||||
opr : self.opr.into()
|
||||
}.into()
|
||||
};
|
||||
if let Some(id) = self.id{
|
||||
ast.with_id(id)
|
||||
} else {
|
||||
ast
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,6 +260,7 @@ impl GeneralizedInfix {
|
||||
let rest = ChainElement {offset,
|
||||
operator : self.opr.clone(),
|
||||
operand : self.argument_operand(),
|
||||
infix_id : self.id,
|
||||
};
|
||||
|
||||
let target_subtree_infix = target.clone().and_then(|arg| {
|
||||
@ -341,15 +355,16 @@ impl Chain {
|
||||
/// Indexing does not skip `None` operands. Function panics, if get index greater than operands
|
||||
/// count.
|
||||
pub fn insert_operand(&mut self, at_index:usize, operand:ArgWithOffset<Ast>) {
|
||||
let offset = operand.offset;
|
||||
let mut operand = Some(operand);
|
||||
let operator = self.operator.clone_ref();
|
||||
let before_target = at_index == 0;
|
||||
let offset = operand.offset;
|
||||
let mut operand = Some(operand);
|
||||
let operator = self.operator.clone_ref();
|
||||
let before_target = at_index == 0;
|
||||
let infix_id:Option<Id> = None;
|
||||
if before_target {
|
||||
std::mem::swap(&mut operand, &mut self.target);
|
||||
self.args.insert(0,ChainElement{operator,operand,offset})
|
||||
self.args.insert(0,ChainElement{operator,operand,offset,infix_id})
|
||||
} else {
|
||||
self.args.insert(at_index-1,ChainElement{operator,operand,offset})
|
||||
self.args.insert(at_index-1,ChainElement{operator,operand,offset,infix_id})
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,7 +395,8 @@ impl Chain {
|
||||
let operator = element.operator;
|
||||
let argument = element.operand;
|
||||
let operands = MarkedOperands{target,argument};
|
||||
let new_infix = GeneralizedInfix::new_from_operands(operands,operator);
|
||||
let id = element.infix_id;
|
||||
let new_infix = GeneralizedInfix::new_from_operands(operands,operator,id);
|
||||
let new_with_offset = ArgWithOffset {
|
||||
arg : new_infix.into_ast(),
|
||||
offset : element.offset,
|
||||
@ -398,8 +414,8 @@ impl Chain {
|
||||
self.fold_arg()
|
||||
}
|
||||
// TODO[ao] the only case when target is none is when chain have None target and empty
|
||||
// arguments list. Such Chain cannot be generated from Ast, but someone could think that
|
||||
// this is still a valid chain. To consider returning error here.
|
||||
// arguments list. Such Chain cannot be generated from Ast, but someone could think that
|
||||
// this is still a valid chain. To consider returning error here.
|
||||
self.target.unwrap().arg
|
||||
}
|
||||
|
||||
@ -429,6 +445,8 @@ pub struct ChainElement {
|
||||
pub operand : Operand,
|
||||
/// Offset between this operand and the next operator.
|
||||
pub offset : usize,
|
||||
/// Id of infix AST which applies this operand.
|
||||
pub infix_id : Option<Id>,
|
||||
}
|
||||
|
||||
impl ChainElement {
|
||||
|
@ -2,22 +2,49 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::Ast;
|
||||
use crate::{Ast, TokenConsumer};
|
||||
use crate::Id;
|
||||
use crate::crumbs::Located;
|
||||
use crate::crumbs::PrefixCrumb;
|
||||
use crate::HasTokens;
|
||||
use crate::known;
|
||||
use crate::Prefix;
|
||||
use crate::Shifted;
|
||||
|
||||
use utils::vec::VecExt;
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
// === Argument ===
|
||||
// ================
|
||||
|
||||
/// Struct representing an element of a Prefix Chain: an argument applied over the function.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Argument {
|
||||
/// An argument ast with offset between it and previous arg or function.
|
||||
pub sast : Shifted<Ast>,
|
||||
/// The id of Prefix AST of this argument application.
|
||||
pub prefix_id : Option<Id>,
|
||||
}
|
||||
|
||||
impl HasTokens for Argument {
|
||||
fn feed_to(&self, consumer: &mut impl TokenConsumer) {
|
||||
self.sast.feed_to(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// === Prefix Chain ===
|
||||
// ====================
|
||||
|
||||
/// Result of flattening a sequence of prefix applications.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Chain {
|
||||
/// The function (initial application target)
|
||||
pub func : Ast,
|
||||
/// Subsequent arguments applied over the function.
|
||||
pub args : Vec<Shifted<Ast>>,
|
||||
pub args : Vec<Argument>,
|
||||
}
|
||||
|
||||
impl Chain {
|
||||
@ -25,12 +52,14 @@ impl Chain {
|
||||
/// App(App(a,b),c) into flat list where first element is the function and
|
||||
/// then arguments are placed: `{func:a, args:[b,c]}`.
|
||||
pub fn new(ast:&known::Prefix) -> Chain {
|
||||
fn run(ast:&known::Prefix, mut acc: &mut Vec<Shifted<Ast>>) -> Ast {
|
||||
fn run(ast:&known::Prefix, mut acc: &mut Vec<Argument>) -> Ast {
|
||||
let func = match known::Prefix::try_from(&ast.func) {
|
||||
Ok(lhs_app) => run(&lhs_app, &mut acc),
|
||||
_ => ast.func.clone(),
|
||||
};
|
||||
acc.push(Shifted{wrapped:ast.arg.clone(),off:ast.off});
|
||||
let sast = Shifted{wrapped:ast.arg.clone(),off:ast.off};
|
||||
let prefix_id = ast.id();
|
||||
acc.push(Argument{sast,prefix_id});
|
||||
func
|
||||
}
|
||||
|
||||
@ -52,9 +81,11 @@ impl Chain {
|
||||
Self::new(prefix)
|
||||
} else if let Ok(ref section) = known::SectionRight::try_from(ast) {
|
||||
// Case like `+ a b`
|
||||
let func = section.opr.clone();
|
||||
let func = section.opr.clone();
|
||||
let right_chain = Chain::new_non_strict(§ion.arg);
|
||||
let mut args = vec![Shifted{wrapped:right_chain.func, off:section.off}];
|
||||
let sast = Shifted{wrapped:right_chain.func, off:section.off};
|
||||
let prefix_id = section.id();
|
||||
let mut args = vec![Argument{sast,prefix_id}];
|
||||
args.extend(right_chain.args);
|
||||
Chain {func,args}
|
||||
} else {
|
||||
@ -87,7 +118,7 @@ impl Chain {
|
||||
let mut i = 0;
|
||||
self.args.iter().map(move |arg| {
|
||||
i += 1;
|
||||
Located::new(&func_crumbs[i..],&arg.wrapped)
|
||||
Located::new(&func_crumbs[i..],&arg.sast.wrapped)
|
||||
})
|
||||
}
|
||||
|
||||
@ -96,11 +127,11 @@ impl Chain {
|
||||
pub fn fold_arg(&mut self) {
|
||||
if let Some(arg) = self.args.pop_front() {
|
||||
let new_prefix = Prefix{
|
||||
arg : arg.wrapped,
|
||||
arg : arg.sast.wrapped,
|
||||
func : self.func.clone_ref(),
|
||||
off : arg.off,
|
||||
off : arg.sast.off,
|
||||
};
|
||||
self.func = new_prefix.into();
|
||||
self.func = Ast::new(new_prefix,arg.prefix_id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,6 +151,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[test]
|
||||
fn prefix_chain() {
|
||||
@ -127,13 +159,15 @@ mod tests {
|
||||
let b = Ast::var("b");
|
||||
let c = Ast::var("c");
|
||||
|
||||
let a_b = Ast::prefix(a.clone(),b.clone());
|
||||
let a_b_c = Ast::prefix(a_b.clone(),c.clone());
|
||||
let a_b = Ast::prefix(a.clone(),b.clone()).with_id(Uuid::new_v4());
|
||||
let a_b_c = Ast::prefix(a_b.clone(),c.clone()).with_id(Uuid::new_v4());
|
||||
|
||||
let chain = Chain::try_new(&a_b_c).unwrap();
|
||||
assert_eq!(chain.func, a);
|
||||
assert_eq!(chain.args[0].wrapped, b);
|
||||
assert_eq!(chain.args[1].wrapped, c);
|
||||
assert_eq!(chain.args[0].sast.wrapped, b);
|
||||
assert_eq!(chain.args[1].sast.wrapped, c);
|
||||
assert_eq!(chain.args[0].prefix_id, a_b.id);
|
||||
assert_eq!(chain.args[1].prefix_id, a_b_c.id);
|
||||
|
||||
let (arg1,arg2) = chain.enumerate_args().expect_tuple();
|
||||
assert_eq!(arg1.item, &b);
|
||||
|
@ -97,9 +97,14 @@ impl Parser {
|
||||
}
|
||||
|
||||
/// Program is expected to be single non-empty line module. The line's AST is
|
||||
/// returned. Panics otherwise.
|
||||
/// returned. Panics otherwise. The program is parsed with empty IdMap.
|
||||
pub fn parse_line(&self, program:impl Str) -> FallibleResult<Ast> {
|
||||
let module = self.parse_module(program,default())?;
|
||||
self.parse_line_with_id_map(program,default())
|
||||
}
|
||||
/// Program is expected to be single non-empty line module. The line's AST is returned. Panics
|
||||
/// otherwise.
|
||||
pub fn parse_line_with_id_map(&self, program:impl Str, id_map:IdMap) -> FallibleResult<Ast> {
|
||||
let module = self.parse_module(program,id_map)?;
|
||||
|
||||
let mut lines = module.lines.clone().into_iter().filter_map(|line| {
|
||||
line.elem
|
||||
|
@ -130,7 +130,10 @@ impl<'a> Implementation for node::Ref<'a> {
|
||||
infix.into_ast()
|
||||
} else {
|
||||
let mut prefix = ast::prefix::Chain::new_non_strict(ast);
|
||||
let item = Shifted{wrapped:new, off:DEFAULT_OFFSET};
|
||||
let item = ast::prefix::Argument{
|
||||
sast : Shifted{wrapped:new, off:DEFAULT_OFFSET},
|
||||
prefix_id : None,
|
||||
};
|
||||
match ins_type {
|
||||
BeforeTarget => prefix.args.insert(0,item),
|
||||
AfterTarget => prefix.args.insert(1,item),
|
||||
|
@ -22,8 +22,9 @@ pub trait Builder : Sized {
|
||||
fn add_child
|
||||
(self, offset:usize, len:usize, kind:node::Kind, crumbs:impl IntoCrumbs) -> ChildBuilder<Self> {
|
||||
let node = Node {kind,
|
||||
size: Size::new(len),
|
||||
children : vec![]
|
||||
size : Size::new(len),
|
||||
children : vec![],
|
||||
expression_id : None,
|
||||
};
|
||||
let child = node::Child { node,
|
||||
offset : Size::new(offset),
|
||||
@ -50,6 +51,12 @@ pub trait Builder : Sized {
|
||||
self.node_being_built().children.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set expression id for this node.
|
||||
fn set_expression_id(mut self, id:ast::Id) -> Self {
|
||||
self.node_being_built().expression_id = Some(id);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,9 +78,10 @@ impl TreeBuilder {
|
||||
pub fn new(len:usize) -> Self {
|
||||
TreeBuilder {
|
||||
built : Node {
|
||||
kind : node::Kind::Root,
|
||||
size : Size::new(len),
|
||||
children : vec![],
|
||||
kind : node::Kind::Root,
|
||||
size : Size::new(len),
|
||||
children : vec![],
|
||||
expression_id : None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,14 +110,15 @@ impl SpanTreeGenerator for Ast {
|
||||
ast::prefix::Chain::try_new(self).unwrap().generate_node(kind),
|
||||
// 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),
|
||||
ast::Shape::Match(_) if ast::macros::as_lambda_match(self).is_none() =>
|
||||
ast::known::Match::try_new(self.clone_ref()).unwrap().generate_node(kind),
|
||||
ast::Shape::Ambiguous(_) =>
|
||||
ast::known::Ambiguous::try_new(self.clone_ref()).unwrap().generate_node(kind),
|
||||
_ => {
|
||||
let size = Size::new(self.len());
|
||||
let children = default();
|
||||
Ok(Node {kind,size,children})
|
||||
let size = Size::new(self.len());
|
||||
let children = default();
|
||||
let expression_id = self.id;
|
||||
Ok(Node {kind,size,children,expression_id})
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -174,9 +175,10 @@ impl SpanTreeGenerator for ast::opr::Chain {
|
||||
}
|
||||
|
||||
Ok((Node {
|
||||
kind : if is_last {kind} else {node::Kind::Chained},
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
kind : if is_last {kind} else {node::Kind::Chained},
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
expression_id : elem.infix_id,
|
||||
}, elem.offset))
|
||||
})?;
|
||||
Ok(node)
|
||||
@ -201,16 +203,17 @@ impl SpanTreeGenerator for ast::prefix::Chain {
|
||||
|
||||
let mut gen = ChildGenerator::default();
|
||||
gen.add_node(vec![Func.into()],node);
|
||||
gen.spacing(arg.off);
|
||||
gen.spacing(arg.sast.off);
|
||||
if let node::Kind::Target {..} = arg_kind {
|
||||
gen.generate_empty_node(InsertType::BeforeTarget);
|
||||
}
|
||||
gen.generate_ast_node(Located::new(Arg,arg.wrapped.clone_ref()), arg_kind)?;
|
||||
gen.generate_ast_node(Located::new(Arg,arg.sast.wrapped.clone_ref()), arg_kind)?;
|
||||
gen.generate_empty_node(InsertType::Append);
|
||||
Ok(Node {
|
||||
kind : if is_last {kind} else {node::Kind::Chained},
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
kind : if is_last {kind} else {node::Kind::Chained},
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
expression_id : arg.prefix_id,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -219,7 +222,7 @@ impl SpanTreeGenerator for ast::prefix::Chain {
|
||||
|
||||
// === Match ===
|
||||
|
||||
impl SpanTreeGenerator for ast::Match<Ast> {
|
||||
impl SpanTreeGenerator for ast::known::Match {
|
||||
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
|
||||
let is_removable = false;
|
||||
let children_kind = node::Kind::Argument {is_removable};
|
||||
@ -239,8 +242,9 @@ impl SpanTreeGenerator for ast::Match<Ast> {
|
||||
generate_children_from_segment(&mut gen,index+1,&segment.wrapped)?;
|
||||
}
|
||||
Ok(Node {kind,
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
expression_id : self.id(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -263,7 +267,7 @@ fn generate_children_from_segment
|
||||
|
||||
// === Ambiguous ==
|
||||
|
||||
impl SpanTreeGenerator for ast::Ambiguous<Ast> {
|
||||
impl SpanTreeGenerator for ast::known::Ambiguous {
|
||||
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
|
||||
let mut gen = ChildGenerator::default();
|
||||
let first_segment_index = 0;
|
||||
@ -273,8 +277,9 @@ impl SpanTreeGenerator for ast::Ambiguous<Ast> {
|
||||
generate_children_from_abiguous_segment(&mut gen, index+1, &segment.wrapped)?;
|
||||
}
|
||||
Ok(Node{kind,
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
size : gen.current_offset,
|
||||
children : gen.children,
|
||||
expression_id : self.id(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -317,17 +322,46 @@ mod test {
|
||||
use parser::Parser;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
use ast::IdMap;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
/// A helper function which removes information about expression id from thw tree rooted at
|
||||
/// `node`.
|
||||
///
|
||||
/// It is used in tests. Because parser can assign id as he pleases, therefore to keep tests
|
||||
/// cleaner the expression ids are removed before comparing trees.
|
||||
fn clear_expression_ids(node:&mut Node) {
|
||||
node.expression_id = None;
|
||||
for child in &mut node.children {
|
||||
clear_expression_ids(&mut child.node);
|
||||
}
|
||||
}
|
||||
|
||||
#[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 is_removable = false;
|
||||
let parser = Parser::new_or_panic();
|
||||
let mut id_map = IdMap::default();
|
||||
id_map.generate(0..15);
|
||||
id_map.generate(0..11);
|
||||
id_map.generate(12..13);
|
||||
id_map.generate(14..15);
|
||||
id_map.generate(4..11);
|
||||
let ast = parser.parse_line_with_id_map("2 + foo bar - 3",id_map.clone()).unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
|
||||
let expected = TreeBuilder::new(15)
|
||||
// Check the expression ids we defined:
|
||||
for id_map_entry in id_map.vec {
|
||||
let (span,id) = id_map_entry;
|
||||
let node = tree.root_ref().find_by_span(&span);
|
||||
assert!(node.is_some(), "Node with span {} not found", span);
|
||||
assert_eq!(node.unwrap().node.expression_id, Some(id));
|
||||
}
|
||||
|
||||
// Check the other fields:
|
||||
clear_expression_ids(&mut tree.root);
|
||||
let is_removable = false;
|
||||
let expected = TreeBuilder::new(15)
|
||||
.add_empty_child(0,BeforeTarget)
|
||||
.add_child(0,11,Target{is_removable},InfixCrumb::LeftOperand)
|
||||
.add_empty_child(0,BeforeTarget)
|
||||
@ -353,12 +387,13 @@ 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 is_removable = true;
|
||||
let parser = Parser::new_or_panic();
|
||||
let ast = parser.parse_line("2 + 3 + foo bar baz 13 + 5").unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
clear_expression_ids(&mut tree.root);
|
||||
|
||||
let expected = TreeBuilder::new(26)
|
||||
let is_removable = true;
|
||||
let expected = TreeBuilder::new(26)
|
||||
.add_child(0,22,Chained,InfixCrumb::LeftOperand)
|
||||
.add_child(0,5,Chained,InfixCrumb::LeftOperand)
|
||||
.add_empty_child(0,BeforeTarget)
|
||||
@ -395,12 +430,13 @@ 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 is_removable = true;
|
||||
let parser = Parser::new_or_panic();
|
||||
let ast = parser.parse_line("1,2,3").unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
clear_expression_ids(&mut tree.root);
|
||||
|
||||
let expected = TreeBuilder::new(5)
|
||||
let is_removable = true;
|
||||
let expected = TreeBuilder::new(5)
|
||||
.add_empty_child(0,Append)
|
||||
.add_leaf (0,1,Argument{is_removable},InfixCrumb::LeftOperand)
|
||||
.add_leaf (1,1,Operation,InfixCrumb::Operator)
|
||||
@ -422,11 +458,12 @@ 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 is_removable = true;
|
||||
let ast = parser.parse_line("+ * + + 2 +").unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
clear_expression_ids(&mut tree.root);
|
||||
|
||||
let expected = TreeBuilder::new(11)
|
||||
let is_removable = true;
|
||||
let expected = TreeBuilder::new(11)
|
||||
.add_child(0,9,Chained,SectionLeftCrumb::Arg)
|
||||
.add_child(0,5,Chained,InfixCrumb::LeftOperand)
|
||||
.add_child(0,3,Chained,SectionLeftCrumb::Arg)
|
||||
@ -455,12 +492,13 @@ 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 is_removable = true;
|
||||
let parser = Parser::new_or_panic();
|
||||
let ast = parser.parse_line(",2,").unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
clear_expression_ids(&mut tree.root);
|
||||
|
||||
let expected = TreeBuilder::new(3)
|
||||
let is_removable = true;
|
||||
let expected = TreeBuilder::new(3)
|
||||
.add_empty_child(0,Append)
|
||||
.add_leaf (0,1,Operation,SectionRightCrumb::Opr)
|
||||
.add_child(1,2,Chained ,SectionRightCrumb::Arg)
|
||||
@ -478,13 +516,22 @@ mod 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 parser = Parser::new_or_panic();
|
||||
let mut id_map = IdMap::default();
|
||||
id_map.generate(0..29);
|
||||
let expression = "if foo then (a + b) x else ()";
|
||||
let ast = parser.parse_line_with_id_map(expression,id_map.clone()).unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
|
||||
let if_then_else_cr = vec![Seq { right: false }, Or, Build];
|
||||
let parens_cr = vec![Seq { right: false }, Or, Or, Build];
|
||||
// Check if expression id is set
|
||||
let (_,expected_id) = id_map.vec.first().unwrap();
|
||||
assert_eq!(tree.root_ref().expression_id,Some(*expected_id));
|
||||
|
||||
// Check the other fields
|
||||
clear_expression_ids(&mut tree.root);
|
||||
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}
|
||||
@ -515,13 +562,21 @@ mod test {
|
||||
|
||||
#[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 parser = Parser::new_or_panic();
|
||||
let mut id_map = IdMap::default();
|
||||
id_map.generate(0..2);
|
||||
let ast = parser.parse_line_with_id_map("(4",id_map.clone()).unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
|
||||
// Check the expression id:
|
||||
let (_,expected_id) = id_map.vec.first().unwrap();
|
||||
assert_eq!(tree.root_ref().expression_id,Some(*expected_id));
|
||||
|
||||
// Check the other fields:
|
||||
clear_expression_ids(&mut tree.root);
|
||||
let is_removable = false;
|
||||
let crumb = AmbiguousCrumb{index:0, field:AmbiguousSegmentCrumb::Body};
|
||||
|
||||
let expected = TreeBuilder::new(2)
|
||||
let expected = TreeBuilder::new(2)
|
||||
.add_leaf(1,1,Argument {is_removable},crumb)
|
||||
.build();
|
||||
|
||||
@ -530,12 +585,13 @@ mod test {
|
||||
|
||||
#[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 parser = Parser::new_or_panic();
|
||||
let ast = parser.parse_line("foo a-> b + c").unwrap();
|
||||
let mut tree = ast.generate_tree().unwrap();
|
||||
clear_expression_ids(&mut tree.root);
|
||||
|
||||
let expected = TreeBuilder::new(13)
|
||||
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)
|
||||
|
@ -86,10 +86,11 @@ impl SpanTree {
|
||||
|
||||
impl Default for SpanTree {
|
||||
fn default() -> Self {
|
||||
let kind = node::Kind::Root;
|
||||
let size = default();
|
||||
let children = default();
|
||||
let root = Node {kind,size,children};
|
||||
let expression_id = None;
|
||||
let kind = node::Kind::Root;
|
||||
let size = default();
|
||||
let children = default();
|
||||
let root = Node {kind,size,children,expression_id};
|
||||
Self {root}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,8 @@ pub enum InsertType {BeforeTarget,AfterTarget,Append}
|
||||
// === Errors ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[fail(display = "The crumb `{}` is invalid, only {} children present. Traversed crumbs: {:?}.", crumb,count,context)]
|
||||
#[fail(display = "The crumb `{}` is invalid, only {} children present. Traversed crumbs: {:?}.",
|
||||
crumb,count,context)]
|
||||
#[derive(Debug,Fail,Clone)]
|
||||
pub struct InvalidCrumb {
|
||||
/// Crumb that was attempted.
|
||||
@ -90,18 +91,20 @@ pub fn parent_crumbs(crumbs:&[Crumb]) -> Option<&[Crumb]> {
|
||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Node {
|
||||
pub kind : Kind,
|
||||
pub size : Size,
|
||||
pub children : Vec<Child>,
|
||||
pub kind : Kind,
|
||||
pub size : Size,
|
||||
pub children : Vec<Child>,
|
||||
pub expression_id : Option<ast::Id>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
/// Create Empty node.
|
||||
pub fn new_empty(insert_type:InsertType) -> Self {
|
||||
Node {
|
||||
kind : Kind::Empty(insert_type),
|
||||
size : Size::new(0),
|
||||
children : Vec::new(),
|
||||
kind : Kind::Empty(insert_type),
|
||||
size : Size::new(0),
|
||||
children : Vec::new(),
|
||||
expression_id : None,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user