The optional Expression Id was added to SpanTree, to allow reading type information by views.

Original commit: b85eeba5aa
This commit is contained in:
Adam Obuchowicz 2020-06-24 09:35:10 +02:00 committed by GitHub
parent cb6a16d402
commit 87326a2f3b
10 changed files with 242 additions and 111 deletions

View File

@ -127,7 +127,7 @@ commands.build.rust = async function(argv) {
console.log('Checking the resulting WASM size.') console.log('Checking the resulting WASM size.')
let stats = fss.statSync(paths.dist.wasm.mainOptGz) 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 let size = Math.round(100 * stats.size / 1024 / 1024) / 100
if (size > limit) { if (size > limit) {
throw(`Output file size exceeds the limit (${size}MB > ${limit}MB).`) throw(`Output file size exceeds the limit (${size}MB > ${limit}MB).`)

View File

@ -75,14 +75,17 @@ impl IdMap {
IdMap {vec} IdMap {vec}
} }
/// Assigns Span to given ID. /// Assigns Span to given ID.
pub fn insert(&mut self, span:Span, id:Id) { pub fn insert(&mut self, span:impl Into<Span>, id:Id) {
self.vec.push((span,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 === // === Errors ===
// ============== // ==============

View File

@ -2,6 +2,7 @@
use crate::prelude::*; use crate::prelude::*;
use crate::Id;
use crate::Infix; use crate::Infix;
use crate::SectionLeft; use crate::SectionLeft;
use crate::SectionRight; use crate::SectionRight;
@ -129,6 +130,8 @@ pub struct GeneralizedInfix {
pub opr : Operator, pub opr : Operator,
/// Right operand, if present. /// Right operand, if present.
pub right : Operand, pub right : Operand,
/// Infix id.
pub id : Option<Id>,
} }
/// A structure used for GeneralizedInfix construction which marks operands as _target_ and /// 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 /// Tries interpret given AST node as GeneralizedInfix. Returns None, if Ast is not any kind of
/// application on infix operator. /// application on infix operator.
pub fn try_new(ast:&Ast) -> Option<GeneralizedInfix> { pub fn try_new(ast:&Ast) -> Option<GeneralizedInfix> {
let id = ast.id;
match ast.shape().clone() { match ast.shape().clone() {
Shape::Infix(infix) => Some(GeneralizedInfix{ Shape::Infix(infix) => Some(GeneralizedInfix{
id,
left : make_operand (infix.larg,infix.loff), left : make_operand (infix.larg,infix.loff),
opr : make_operator(&infix.opr)?, opr : make_operator(&infix.opr)?,
right : make_operand (infix.rarg,infix.roff), right : make_operand (infix.rarg,infix.roff),
}), }),
Shape::SectionLeft(left) => Some(GeneralizedInfix{ Shape::SectionLeft(left) => Some(GeneralizedInfix{
id,
left : make_operand (left.arg,left.off), left : make_operand (left.arg,left.off),
opr : make_operator(&left.opr)?, opr : make_operator(&left.opr)?,
right : None, right : None,
}), }),
Shape::SectionRight(right) => Some(GeneralizedInfix{ Shape::SectionRight(right) => Some(GeneralizedInfix{
id,
left : None, left : None,
opr : make_operator(&right.opr)?, opr : make_operator(&right.opr)?,
right : make_operand (right.arg,right.off), right : make_operand (right.arg,right.off),
}), }),
Shape::SectionSides(sides) => Some(GeneralizedInfix{ Shape::SectionSides(sides) => Some(GeneralizedInfix{
id,
left : None, left : None,
opr : make_operator(&sides.opr)?, opr : make_operator(&sides.opr)?,
right : None, right : None,
@ -170,13 +178,13 @@ impl GeneralizedInfix {
} }
/// Constructor with operands marked as target and argument. /// 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) { match assoc(&opr) {
Assoc::Left => GeneralizedInfix {opr, Assoc::Left => GeneralizedInfix {opr,id,
left : operands.target, left : operands.target,
right : operands.argument, right : operands.argument,
}, },
Assoc::Right => GeneralizedInfix {opr, Assoc::Right => GeneralizedInfix {opr,id,
left : operands.argument, left : operands.argument,
right : operands.target, right : operands.target,
}, },
@ -185,7 +193,7 @@ impl GeneralizedInfix {
/// Convert to AST node. /// Convert to AST node.
pub fn into_ast(self) -> Ast { pub fn into_ast(self) -> Ast {
match (self.left,self.right) { let ast:Ast = match (self.left,self.right) {
(Some(left),Some(right)) => Infix{ (Some(left),Some(right)) => Infix{
larg : left.arg, larg : left.arg,
loff : left.offset, loff : left.offset,
@ -206,6 +214,11 @@ impl GeneralizedInfix {
(None,None) => SectionSides { (None,None) => SectionSides {
opr : self.opr.into() opr : self.opr.into()
}.into() }.into()
};
if let Some(id) = self.id{
ast.with_id(id)
} else {
ast
} }
} }
@ -247,6 +260,7 @@ impl GeneralizedInfix {
let rest = ChainElement {offset, let rest = ChainElement {offset,
operator : self.opr.clone(), operator : self.opr.clone(),
operand : self.argument_operand(), operand : self.argument_operand(),
infix_id : self.id,
}; };
let target_subtree_infix = target.clone().and_then(|arg| { 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 /// Indexing does not skip `None` operands. Function panics, if get index greater than operands
/// count. /// count.
pub fn insert_operand(&mut self, at_index:usize, operand:ArgWithOffset<Ast>) { pub fn insert_operand(&mut self, at_index:usize, operand:ArgWithOffset<Ast>) {
let offset = operand.offset; let offset = operand.offset;
let mut operand = Some(operand); let mut operand = Some(operand);
let operator = self.operator.clone_ref(); let operator = self.operator.clone_ref();
let before_target = at_index == 0; let before_target = at_index == 0;
let infix_id:Option<Id> = None;
if before_target { if before_target {
std::mem::swap(&mut operand, &mut self.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 { } 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 operator = element.operator;
let argument = element.operand; let argument = element.operand;
let operands = MarkedOperands{target,argument}; 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 { let new_with_offset = ArgWithOffset {
arg : new_infix.into_ast(), arg : new_infix.into_ast(),
offset : element.offset, offset : element.offset,
@ -398,8 +414,8 @@ impl Chain {
self.fold_arg() self.fold_arg()
} }
// TODO[ao] the only case when target is none is when chain have None target and empty // 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 // 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. // this is still a valid chain. To consider returning error here.
self.target.unwrap().arg self.target.unwrap().arg
} }
@ -429,6 +445,8 @@ pub struct ChainElement {
pub operand : Operand, pub operand : Operand,
/// Offset between this operand and the next operator. /// Offset between this operand and the next operator.
pub offset : usize, pub offset : usize,
/// Id of infix AST which applies this operand.
pub infix_id : Option<Id>,
} }
impl ChainElement { impl ChainElement {

View File

@ -2,22 +2,49 @@
use crate::prelude::*; use crate::prelude::*;
use crate::Ast; use crate::{Ast, TokenConsumer};
use crate::Id;
use crate::crumbs::Located; use crate::crumbs::Located;
use crate::crumbs::PrefixCrumb; use crate::crumbs::PrefixCrumb;
use crate::HasTokens;
use crate::known; use crate::known;
use crate::Prefix; use crate::Prefix;
use crate::Shifted; use crate::Shifted;
use utils::vec::VecExt; use utils::vec::VecExt;
// ================
// === Argument ===
// ================
/// Struct representing an element of a Prefix Chain: an argument applied over the function.
#[derive(Clone,Debug)] #[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. /// Result of flattening a sequence of prefix applications.
#[derive(Clone,Debug)]
pub struct Chain { pub struct Chain {
/// The function (initial application target) /// The function (initial application target)
pub func : Ast, pub func : Ast,
/// Subsequent arguments applied over the function. /// Subsequent arguments applied over the function.
pub args : Vec<Shifted<Ast>>, pub args : Vec<Argument>,
} }
impl Chain { impl Chain {
@ -25,12 +52,14 @@ impl Chain {
/// App(App(a,b),c) into flat list where first element is the function and /// App(App(a,b),c) into flat list where first element is the function and
/// then arguments are placed: `{func:a, args:[b,c]}`. /// then arguments are placed: `{func:a, args:[b,c]}`.
pub fn new(ast:&known::Prefix) -> Chain { 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) { let func = match known::Prefix::try_from(&ast.func) {
Ok(lhs_app) => run(&lhs_app, &mut acc), Ok(lhs_app) => run(&lhs_app, &mut acc),
_ => ast.func.clone(), _ => 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 func
} }
@ -52,9 +81,11 @@ impl Chain {
Self::new(prefix) Self::new(prefix)
} else if let Ok(ref section) = known::SectionRight::try_from(ast) { } else if let Ok(ref section) = known::SectionRight::try_from(ast) {
// Case like `+ a b` // Case like `+ a b`
let func = section.opr.clone(); let func = section.opr.clone();
let right_chain = Chain::new_non_strict(&section.arg); let right_chain = Chain::new_non_strict(&section.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); args.extend(right_chain.args);
Chain {func,args} Chain {func,args}
} else { } else {
@ -87,7 +118,7 @@ impl Chain {
let mut i = 0; let mut i = 0;
self.args.iter().map(move |arg| { self.args.iter().map(move |arg| {
i += 1; 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) { pub fn fold_arg(&mut self) {
if let Some(arg) = self.args.pop_front() { if let Some(arg) = self.args.pop_front() {
let new_prefix = Prefix{ let new_prefix = Prefix{
arg : arg.wrapped, arg : arg.sast.wrapped,
func : self.func.clone_ref(), 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 super::*;
use utils::test::ExpectTuple; use utils::test::ExpectTuple;
use uuid::Uuid;
#[test] #[test]
fn prefix_chain() { fn prefix_chain() {
@ -127,13 +159,15 @@ mod tests {
let b = Ast::var("b"); let b = Ast::var("b");
let c = Ast::var("c"); let c = Ast::var("c");
let a_b = Ast::prefix(a.clone(),b.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()); 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(); let chain = Chain::try_new(&a_b_c).unwrap();
assert_eq!(chain.func, a); assert_eq!(chain.func, a);
assert_eq!(chain.args[0].wrapped, b); assert_eq!(chain.args[0].sast.wrapped, b);
assert_eq!(chain.args[1].wrapped, c); 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(); let (arg1,arg2) = chain.enumerate_args().expect_tuple();
assert_eq!(arg1.item, &b); assert_eq!(arg1.item, &b);

View File

@ -97,9 +97,14 @@ impl Parser {
} }
/// Program is expected to be single non-empty line module. The line's AST is /// 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> { 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| { let mut lines = module.lines.clone().into_iter().filter_map(|line| {
line.elem line.elem

View File

@ -130,7 +130,10 @@ impl<'a> Implementation for node::Ref<'a> {
infix.into_ast() infix.into_ast()
} else { } else {
let mut prefix = ast::prefix::Chain::new_non_strict(ast); 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 { match ins_type {
BeforeTarget => prefix.args.insert(0,item), BeforeTarget => prefix.args.insert(0,item),
AfterTarget => prefix.args.insert(1,item), AfterTarget => prefix.args.insert(1,item),

View File

@ -22,8 +22,9 @@ pub trait Builder : Sized {
fn add_child fn add_child
(self, offset:usize, len:usize, kind:node::Kind, crumbs:impl IntoCrumbs) -> ChildBuilder<Self> { (self, offset:usize, len:usize, kind:node::Kind, crumbs:impl IntoCrumbs) -> ChildBuilder<Self> {
let node = Node {kind, let node = Node {kind,
size: Size::new(len), size : Size::new(len),
children : vec![] children : vec![],
expression_id : None,
}; };
let child = node::Child { node, let child = node::Child { node,
offset : Size::new(offset), offset : Size::new(offset),
@ -50,6 +51,12 @@ pub trait Builder : Sized {
self.node_being_built().children.push(child); self.node_being_built().children.push(child);
self 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 { pub fn new(len:usize) -> Self {
TreeBuilder { TreeBuilder {
built : Node { built : Node {
kind : node::Kind::Root, kind : node::Kind::Root,
size : Size::new(len), size : Size::new(len),
children : vec![], children : vec![],
expression_id : None,
} }
} }
} }

View File

@ -110,14 +110,15 @@ impl SpanTreeGenerator for Ast {
ast::prefix::Chain::try_new(self).unwrap().generate_node(kind), ast::prefix::Chain::try_new(self).unwrap().generate_node(kind),
// Lambdas should fall in _ case, because we don't want to create subports for // Lambdas should fall in _ case, because we don't want to create subports for
// them // them
ast::Shape::Match(ast) if ast::macros::as_lambda_match(self).is_none() => ast::Shape::Match(_) if ast::macros::as_lambda_match(self).is_none() =>
ast.generate_node(kind), ast::known::Match::try_new(self.clone_ref()).unwrap().generate_node(kind),
ast::Shape::Ambiguous(ast) => ast::Shape::Ambiguous(_) =>
ast.generate_node(kind), ast::known::Ambiguous::try_new(self.clone_ref()).unwrap().generate_node(kind),
_ => { _ => {
let size = Size::new(self.len()); let size = Size::new(self.len());
let children = default(); let children = default();
Ok(Node {kind,size,children}) let expression_id = self.id;
Ok(Node {kind,size,children,expression_id})
}, },
} }
} }
@ -174,9 +175,10 @@ impl SpanTreeGenerator for ast::opr::Chain {
} }
Ok((Node { Ok((Node {
kind : if is_last {kind} else {node::Kind::Chained}, kind : if is_last {kind} else {node::Kind::Chained},
size : gen.current_offset, size : gen.current_offset,
children : gen.children, children : gen.children,
expression_id : elem.infix_id,
}, elem.offset)) }, elem.offset))
})?; })?;
Ok(node) Ok(node)
@ -201,16 +203,17 @@ impl SpanTreeGenerator for ast::prefix::Chain {
let mut gen = ChildGenerator::default(); let mut gen = ChildGenerator::default();
gen.add_node(vec![Func.into()],node); gen.add_node(vec![Func.into()],node);
gen.spacing(arg.off); gen.spacing(arg.sast.off);
if let node::Kind::Target {..} = arg_kind { if let node::Kind::Target {..} = arg_kind {
gen.generate_empty_node(InsertType::BeforeTarget); 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); gen.generate_empty_node(InsertType::Append);
Ok(Node { Ok(Node {
kind : if is_last {kind} else {node::Kind::Chained}, kind : if is_last {kind} else {node::Kind::Chained},
size : gen.current_offset, size : gen.current_offset,
children : gen.children, children : gen.children,
expression_id : arg.prefix_id,
}) })
}) })
} }
@ -219,7 +222,7 @@ impl SpanTreeGenerator for ast::prefix::Chain {
// === Match === // === Match ===
impl SpanTreeGenerator for ast::Match<Ast> { impl SpanTreeGenerator for ast::known::Match {
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> { fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
let is_removable = false; let is_removable = false;
let children_kind = node::Kind::Argument {is_removable}; 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)?; generate_children_from_segment(&mut gen,index+1,&segment.wrapped)?;
} }
Ok(Node {kind, Ok(Node {kind,
size : gen.current_offset, size : gen.current_offset,
children : gen.children, children : gen.children,
expression_id : self.id(),
}) })
} }
} }
@ -263,7 +267,7 @@ fn generate_children_from_segment
// === Ambiguous == // === Ambiguous ==
impl SpanTreeGenerator for ast::Ambiguous<Ast> { impl SpanTreeGenerator for ast::known::Ambiguous {
fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> { fn generate_node(&self, kind:node::Kind) -> FallibleResult<Node> {
let mut gen = ChildGenerator::default(); let mut gen = ChildGenerator::default();
let first_segment_index = 0; 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)?; generate_children_from_abiguous_segment(&mut gen, index+1, &segment.wrapped)?;
} }
Ok(Node{kind, Ok(Node{kind,
size : gen.current_offset, size : gen.current_offset,
children : gen.children, children : gen.children,
expression_id : self.id(),
}) })
} }
} }
@ -317,17 +322,46 @@ mod test {
use parser::Parser; use parser::Parser;
use wasm_bindgen_test::wasm_bindgen_test; use wasm_bindgen_test::wasm_bindgen_test;
use wasm_bindgen_test::wasm_bindgen_test_configure; use wasm_bindgen_test::wasm_bindgen_test_configure;
use ast::IdMap;
wasm_bindgen_test_configure!(run_in_browser); 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] #[wasm_bindgen_test]
fn generating_span_tree() { fn generating_span_tree() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("2 + foo bar - 3").unwrap(); let mut id_map = IdMap::default();
let tree = ast.generate_tree().unwrap(); id_map.generate(0..15);
let is_removable = false; 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_empty_child(0,BeforeTarget)
.add_child(0,11,Target{is_removable},InfixCrumb::LeftOperand) .add_child(0,11,Target{is_removable},InfixCrumb::LeftOperand)
.add_empty_child(0,BeforeTarget) .add_empty_child(0,BeforeTarget)
@ -353,12 +387,13 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn generate_span_tree_with_chains() { fn generate_span_tree_with_chains() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("2 + 3 + foo bar baz 13 + 5").unwrap(); let ast = parser.parse_line("2 + 3 + foo bar baz 13 + 5").unwrap();
let tree = ast.generate_tree().unwrap(); let mut tree = ast.generate_tree().unwrap();
let is_removable = true; 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,22,Chained,InfixCrumb::LeftOperand)
.add_child(0,5,Chained,InfixCrumb::LeftOperand) .add_child(0,5,Chained,InfixCrumb::LeftOperand)
.add_empty_child(0,BeforeTarget) .add_empty_child(0,BeforeTarget)
@ -395,12 +430,13 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn generating_span_tree_from_right_assoc_operator() { fn generating_span_tree_from_right_assoc_operator() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("1,2,3").unwrap(); let ast = parser.parse_line("1,2,3").unwrap();
let tree = ast.generate_tree().unwrap(); let mut tree = ast.generate_tree().unwrap();
let is_removable = true; 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_empty_child(0,Append)
.add_leaf (0,1,Argument{is_removable},InfixCrumb::LeftOperand) .add_leaf (0,1,Argument{is_removable},InfixCrumb::LeftOperand)
.add_leaf (1,1,Operation,InfixCrumb::Operator) .add_leaf (1,1,Operation,InfixCrumb::Operator)
@ -422,11 +458,12 @@ mod test {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
// The star makes `SectionSides` ast being one of the parameters of + chain. First + makes // The star makes `SectionSides` ast being one of the parameters of + chain. First + makes
// SectionRight, and last + makes SectionLeft. // SectionRight, and last + makes SectionLeft.
let ast = parser.parse_line("+ * + + 2 +").unwrap(); let ast = parser.parse_line("+ * + + 2 +").unwrap();
let tree = ast.generate_tree().unwrap(); let mut tree = ast.generate_tree().unwrap();
let is_removable = true; 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,9,Chained,SectionLeftCrumb::Arg)
.add_child(0,5,Chained,InfixCrumb::LeftOperand) .add_child(0,5,Chained,InfixCrumb::LeftOperand)
.add_child(0,3,Chained,SectionLeftCrumb::Arg) .add_child(0,3,Chained,SectionLeftCrumb::Arg)
@ -455,12 +492,13 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn generating_span_tree_from_right_assoc_section() { fn generating_span_tree_from_right_assoc_section() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line(",2,").unwrap(); let ast = parser.parse_line(",2,").unwrap();
let tree = ast.generate_tree().unwrap(); let mut tree = ast.generate_tree().unwrap();
let is_removable = true; 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_empty_child(0,Append)
.add_leaf (0,1,Operation,SectionRightCrumb::Opr) .add_leaf (0,1,Operation,SectionRightCrumb::Opr)
.add_child(1,2,Chained ,SectionRightCrumb::Arg) .add_child(1,2,Chained ,SectionRightCrumb::Arg)
@ -478,13 +516,22 @@ mod test {
fn generating_span_tree_from_matched_macros() { fn generating_span_tree_from_matched_macros() {
use PatternMatchCrumb::*; use PatternMatchCrumb::*;
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("if foo then (a + b) x else ()").unwrap(); let mut id_map = IdMap::default();
let tree = ast.generate_tree().unwrap(); id_map.generate(0..29);
let is_removable = false; 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]; // Check if expression id is set
let parens_cr = vec![Seq { right: false }, Or, Or, Build]; 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 segment_body_crumbs = |index:usize, pattern_crumb:&Vec<PatternMatchCrumb>| {
let val = ast::crumbs::SegmentMatchCrumb::Body {val:pattern_crumb.clone()}; let val = ast::crumbs::SegmentMatchCrumb::Body {val:pattern_crumb.clone()};
ast::crumbs::MatchCrumb::Segs {val,index} ast::crumbs::MatchCrumb::Segs {val,index}
@ -515,13 +562,21 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn generating_span_tree_from_ambiguous_macros() { fn generating_span_tree_from_ambiguous_macros() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("(4").unwrap(); let mut id_map = IdMap::default();
let tree = ast.generate_tree().unwrap(); 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 is_removable = false;
let crumb = AmbiguousCrumb{index:0, field:AmbiguousSegmentCrumb::Body}; 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) .add_leaf(1,1,Argument {is_removable},crumb)
.build(); .build();
@ -530,12 +585,13 @@ mod test {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn generating_span_tree_for_lambda() { fn generating_span_tree_for_lambda() {
let parser = Parser::new_or_panic(); let parser = Parser::new_or_panic();
let ast = parser.parse_line("foo a-> b + c").unwrap(); let ast = parser.parse_line("foo a-> b + c").unwrap();
let tree = ast.generate_tree().unwrap(); let mut tree = ast.generate_tree().unwrap();
let is_removable = false; 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_leaf(0,3,Operation,PrefixCrumb::Func)
.add_empty_child(4,BeforeTarget) .add_empty_child(4,BeforeTarget)
.add_leaf(4,9,Target{is_removable},PrefixCrumb::Arg) .add_leaf(4,9,Target{is_removable},PrefixCrumb::Arg)

View File

@ -86,10 +86,11 @@ impl SpanTree {
impl Default for SpanTree { impl Default for SpanTree {
fn default() -> Self { fn default() -> Self {
let kind = node::Kind::Root; let expression_id = None;
let size = default(); let kind = node::Kind::Root;
let children = default(); let size = default();
let root = Node {kind,size,children}; let children = default();
let root = Node {kind,size,children,expression_id};
Self {root} Self {root}
} }
} }

View File

@ -59,7 +59,8 @@ pub enum InsertType {BeforeTarget,AfterTarget,Append}
// === Errors === // === Errors ===
#[allow(missing_docs)] #[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)] #[derive(Debug,Fail,Clone)]
pub struct InvalidCrumb { pub struct InvalidCrumb {
/// Crumb that was attempted. /// Crumb that was attempted.
@ -90,18 +91,20 @@ pub fn parent_crumbs(crumbs:&[Crumb]) -> Option<&[Crumb]> {
#[derive(Clone,Debug,Eq,PartialEq)] #[derive(Clone,Debug,Eq,PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct Node { pub struct Node {
pub kind : Kind, pub kind : Kind,
pub size : Size, pub size : Size,
pub children : Vec<Child>, pub children : Vec<Child>,
pub expression_id : Option<ast::Id>,
} }
impl Node { impl Node {
/// Create Empty node. /// Create Empty node.
pub fn new_empty(insert_type:InsertType) -> Self { pub fn new_empty(insert_type:InsertType) -> Self {
Node { Node {
kind : Kind::Empty(insert_type), kind : Kind::Empty(insert_type),
size : Size::new(0), size : Size::new(0),
children : Vec::new(), children : Vec::new(),
expression_id : None,
} }
} }