Skip to content

Commit

Permalink
Argument Names in the Span Tree (enso-org/ide#781)
Browse files Browse the repository at this point in the history
Original commit: enso-org/ide@04879bd
  • Loading branch information
mwu-tow authored Sep 11, 2020
1 parent d47bfa2 commit 7e0d145
Show file tree
Hide file tree
Showing 26 changed files with 1,403 additions and 221 deletions.
20 changes: 14 additions & 6 deletions ide/src/rust/ide/lib/ast/impl/src/opr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,16 +343,16 @@ impl Chain {
(infix.name() == operator).as_some_from(|| infix.flatten())
}

/// Iterates over &Located<Ast>, beginning with target (this argument) and then subsequent
/// Iterates over operands beginning with target (this argument) and then subsequent
/// arguments.
pub fn enumerate_operands<'a>
(&'a self) -> impl Iterator<Item=Located<&'a ArgWithOffset<Ast>>> + 'a {
(&'a self) -> impl Iterator<Item=Option<Located<&'a ArgWithOffset<Ast>>>> + 'a {
let rev_args = self.args.iter().rev();
let target_crumbs = rev_args.map(ChainElement::crumb_to_previous).collect_vec();
let target = self.target.as_ref();
let loc_target = target.map(|opr| Located::new(target_crumbs,opr)).into_iter();
let loc_target = std::iter::once(target.map(|opr| Located::new(target_crumbs,opr)));
let args = self.args.iter().enumerate();
let loc_args = args.filter_map(move |(i,elem)| {
let loc_args = args.map(move |(i,elem)| {
elem.operand.as_ref().map(|operand| {
let latter_args = self.args.iter().skip(i+1);
let to_infix = latter_args.rev().map(ChainElement::crumb_to_previous);
Expand All @@ -364,6 +364,14 @@ impl Chain {
loc_target.chain(loc_args)
}


/// Iterates over non-empty operands beginning with target (this argument) and then subsequent
/// arguments.
pub fn enumerate_non_empty_operands<'a>
(&'a self) -> impl Iterator<Item=Located<&'a ArgWithOffset<Ast>>> + 'a {
self.enumerate_operands().flatten()
}

/// Iterates over all operator's AST in this chain, starting from target side.
pub fn enumerate_operators<'a>(&'a self) -> impl Iterator<Item=Located<&'a known::Opr>> + 'a {
self.args.iter().enumerate().map(move |(i,elem)| {
Expand Down Expand Up @@ -522,8 +530,8 @@ mod tests {
}

fn test_enumerating(chain:&Chain, root_ast:&Ast, expected_asts:&[&Ast]) {
assert_eq!(chain.enumerate_operands().count(), expected_asts.len());
for (elem,expected) in chain.enumerate_operands().zip(expected_asts) {
assert_eq!(chain.enumerate_non_empty_operands().count(), expected_asts.len());
for (elem,expected) in chain.enumerate_non_empty_operands().zip(expected_asts) {
assert_eq!(elem.item.arg,**expected);
let ast = root_ast.get_traversing(&elem.crumbs).unwrap();
assert_eq!(ast,*expected);
Expand Down
69 changes: 69 additions & 0 deletions ide/src/rust/ide/lib/ast/impl/src/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,22 @@ pub struct Argument {
pub prefix_id : Option<Id>,
}

impl Argument {
/// Make an argument consisting of a single blank placeholder: `_`.
pub fn new_blank(offset:usize, prefix_id:Option<Id>) -> Self {
let sast = Shifted::new(offset,Ast::blank());
Self {sast,prefix_id}
}
}

impl HasTokens for Argument {
fn feed_to(&self, consumer: &mut impl TokenConsumer) {
self.sast.feed_to(consumer)
}
}



// ====================
// === Prefix Chain ===
// ====================
Expand Down Expand Up @@ -156,6 +166,27 @@ impl Chain {
}
self.func
}

/// Get the ID of the Ast represented by this chain.
pub fn id(&self) -> Option<Id> {
match self.args.last() {
Some(last_arg) => last_arg.prefix_id,
None => self.func.id,
}
}

/// Insert argument at given position in the prefix chain. If index is out of bounds,
/// additional blank `_` arguments will be placed.
pub fn insert_arg(&mut self, index:usize, argument:Argument) {
if let Some(blanks_to_add) = index.checked_sub(self.args.len()) {
let make_blank = || {
let prefix_id = argument.prefix_id.map(|_| Id::new_v4());
Argument::new_blank(argument.sast.off,prefix_id)
};
self.args.extend(std::iter::repeat_with(make_blank).take(blanks_to_add));
}
self.args.insert(index,argument);
}
}

impl HasTokens for Chain {
Expand Down Expand Up @@ -209,5 +240,43 @@ mod tests {
assert_eq!(chain.into_ast().repr(), "a b c");
}

#[test]
fn inserting_arg() {
let a = Ast::var("a");
let b = Ast::var("b");
let c = Ast::var("c");
let chain = Chain::new(a,vec![b,c]);
assert_eq!(chain.repr(), "a b c");

let arg = |text:&str| Argument {
prefix_id : None,
sast : Shifted::new(1,Ast::var(text)),
};

{
let mut chain = chain.clone();
chain.insert_arg(0, arg("arg"));
assert_eq!(chain.repr(), "a arg b c");
}

{
let mut chain = chain.clone();
chain.insert_arg(2, arg("arg"));
assert_eq!(chain.repr(), "a b c arg");
}

{
let mut chain = chain.clone();
chain.insert_arg(3, arg("arg"));
assert_eq!(chain.repr(), "a b c _ arg");
}

{
let mut chain = chain.clone();
chain.insert_arg(4, arg("arg"));
assert_eq!(chain.repr(), "a b c _ _ arg");
}
}

// TODO[ao] add tests for modifying chain.
}
10 changes: 10 additions & 0 deletions ide/src/rust/ide/lib/enso-protocol/src/language_server/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,4 +656,14 @@ pub mod test {
method_pointer : None,
}
}

/// Generate `ExpressionValueUpdate` with update for a single expression bringing only the
/// method pointer.
pub fn value_update_with_method_ptr(id:ExpressionId, method_pointer:SuggestionId) -> ExpressionValueUpdate {
ExpressionValueUpdate {
expression_id : id,
typename : None,
method_pointer : Some(method_pointer),
}
}
}
21 changes: 21 additions & 0 deletions ide/src/rust/ide/lib/parser/tests/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ fn assert_opr<StringLike:Into<String>>(ast:&Ast, name:StringLike) {
assert_eq!(*actual,expected);
}

fn roundtrip_program(program:&str) {
let parser = parser::Parser::new_or_panic();
let ast = parser.parse(program.to_string(), Default::default()).unwrap();
assert_eq!(ast.repr(), program, "{:#?}", ast);
}



// ===============
Expand Down Expand Up @@ -436,3 +442,18 @@ impl Fixture {
fn parser_tests() {
Fixture::new().run()
}

/// Test case for https://github.com/enso-org/ide/issues/296
#[wasm_bindgen_test]
fn block_roundtrip() {
let programs = vec![
"main = 10 + 10",
"main =\n a = 10\n b = 20\n a * b",
"main =\n foo a =\n a * 10\n foo 10\n print \"hello\"",
"main =\n foo\n \n bar",
"main =\n \n foo\n \n bar"
];
for program in programs {
roundtrip_program(program);
}
}
50 changes: 43 additions & 7 deletions ide/src/rust/ide/lib/span-tree/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ast::crumbs::*;
use ast::opr::ArgWithOffset;



/// ==============
/// === Errors ===
/// ==============
Expand Down Expand Up @@ -126,6 +127,8 @@ impl<'a> Implementation for node::Ref<'a> {
BeforeTarget | AfterTarget => infix.target = Some(item),
Append if has_arg => infix.push_operand(item),
Append => *last_arg = Some(item),
// TODO? below should never happen, as operator arity is always fixed to 2?
ExpectedArgument(i) => infix.insert_operand(*i, item),
};
infix.into_ast()
} else {
Expand All @@ -135,9 +138,10 @@ impl<'a> Implementation for node::Ref<'a> {
prefix_id : None,
};
match ins_type {
BeforeTarget => prefix.args.insert(0,item),
AfterTarget => prefix.args.insert(1,item),
Append => prefix.args.push(item),
BeforeTarget => prefix.insert_arg(0,item),
AfterTarget => prefix.insert_arg(1,item),
Append => prefix.args.push(item),
ExpectedArgument(i) => prefix.insert_arg(*i, item),
}
prefix.into_ast()
};
Expand Down Expand Up @@ -197,12 +201,18 @@ mod test {

use Action::*;

use wasm_bindgen_test::wasm_bindgen_test;
use parser::Parser;
use crate::builder::TreeBuilder;
use crate::generate::context;
use crate::node::Kind::Operation;
use crate::node::Kind::Target;
use crate::node::InsertType::ExpectedArgument;

use ast::HasRepr;
use data::text::Index;
use data::text::Span;
use std::ops::Range;
use parser::Parser;
use wasm_bindgen_test::wasm_bindgen_test;

#[wasm_bindgen_test]
fn actions_in_span_tree() {
Expand All @@ -217,7 +227,7 @@ mod test {
impl Case {
fn run(&self, parser:&Parser) {
let ast = parser.parse_line(self.expr).unwrap();
let tree = ast.generate_tree().unwrap();
let tree = ast.generate_tree(&context::Empty).unwrap();
let span_begin = Index::new(self.span.start);
let span_end = Index::new(self.span.end);
let span = Span::from_indices(span_begin,span_end);
Expand Down Expand Up @@ -299,7 +309,7 @@ mod test {
impl Case {
fn run(&self, parser:&Parser) {
let ast = parser.parse_line(self.expr).unwrap();
let tree = ast.generate_tree().unwrap();
let tree = ast.generate_tree(&context::Empty).unwrap();
let span_begin = Index::new(self.span.start);
let span_end = Index::new(self.span.end);
let span = Span::from_indices(span_begin,span_end);
Expand Down Expand Up @@ -345,4 +355,30 @@ mod test {
let parser = Parser::new_or_panic();
for case in cases { case.run(&parser); }
}

#[test]
fn setting_positional_arguments() {
// Consider Span Tree for `foo bar` where `foo` is a method known to take 3 parameters.
// We can try setting each of 3 arguments to `baz`.
let is_removable = false;
let tree = TreeBuilder::new(7)
.add_leaf(0,3,Operation ,PrefixCrumb::Func)
.add_leaf(4,7,Target{is_removable},PrefixCrumb::Arg)
.add_empty_child(7, ExpectedArgument(1))
.add_empty_child(7, ExpectedArgument(2))
.build();

let ast = Ast::prefix(Ast::var("foo"), Ast::var("bar"));
assert_eq!(ast.repr(),"foo bar");
let baz = Ast::var("baz");

let after = tree.root_ref().child(1).unwrap().set(&ast,baz.clone_ref()).unwrap();
assert_eq!(after.repr(),"foo baz");

let after = tree.root_ref().child(2).unwrap().set(&ast,baz.clone_ref()).unwrap();
assert_eq!(after.repr(),"foo bar baz");

let after = tree.root_ref().child(3).unwrap().set(&ast,baz.clone_ref()).unwrap();
assert_eq!(after.repr(),"foo bar _ baz");
}
}
2 changes: 2 additions & 0 deletions ide/src/rust/ide/lib/span-tree/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub trait Builder : Sized {
size : Size::new(len),
children : vec![],
expression_id : None,
parameter_info: None,
};
let child = node::Child { node,
offset : Size::new(offset),
Expand Down Expand Up @@ -82,6 +83,7 @@ impl TreeBuilder {
size : Size::new(len),
children : vec![],
expression_id : None,
parameter_info: None,
}
}
}
Expand Down
Loading

0 comments on commit 7e0d145

Please sign in to comment.