mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 01:36:33 +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.')
|
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).`)
|
||||||
|
@ -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 ===
|
||||||
// ==============
|
// ==============
|
||||||
|
@ -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 {
|
||||||
|
@ -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(§ion.arg);
|
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);
|
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);
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user