Original commit: 9ee7f25a9d
This commit is contained in:
Josef 2020-05-19 13:27:48 +02:00 committed by GitHub
parent d65e813541
commit 963ebfc382
8 changed files with 81 additions and 74 deletions

View File

@ -1845,20 +1845,21 @@ mod tests {
// === Match ===
fn match_() -> Match<Ast> {
let var = Ast::var("");
let pat = Rc::new(MacroPatternRaw::Nothing(MacroPatternRawNothing{}));
let tok = Rc::new(MacroPatternMatchRaw::Tok(MacroPatternMatchRawTok{
pat : MacroPatternRawTok{spaced:None, ast:Ast::var("")},
elem : Shifted{off:0,wrapped:Ast::var("")},
pat : MacroPatternRawTok{spaced:None, ast:var.clone()},
elem : Shifted{off:0,wrapped:var.clone()},
}));
let body = Rc::new(MacroPatternMatchRaw::Seq(MacroPatternMatchRawSeq{
pat : MacroPatternRawSeq{pat1:pat.clone(),pat2:pat.clone()},
elem : (tok.clone(),tok.clone()),
}));
let segs = ShiftedVec1 {
head : MacroMatchSegment{head:Ast::var(""),body:body.clone()},
head : MacroMatchSegment{head:var.clone(),body:body.clone()},
tail : vec![]
};
Match{pfx:Some(body),segs,resolved:Ast::var("")}
Match{pfx:Some(body),segs,resolved:var.clone()}
}
#[test]
@ -1897,21 +1898,22 @@ mod tests {
let crumb6 = MatchCrumb::Segs{val:SegmentMatchCrumb::Body{val:crumb1},index:0};
let crumb7 = MatchCrumb::Segs{val:SegmentMatchCrumb::Body{val:crumb2},index:0};
let match1 = match_();
let match2 = match1.set(&crumb3,Ast::var("X")).unwrap();
let match3 = match2.set(&crumb5,Ast::var("Y")).unwrap();
let match4 = match3.set(&crumb7,Ast::var("Z")).unwrap();
let ast = [match1.resolved.clone(), Ast::var("X"), Ast::var("Y"), Ast::var("Z")];
let match2 = match1.set(&crumb3,ast[1].clone()).unwrap();
let match3 = match2.set(&crumb5,ast[2].clone()).unwrap();
let match4 = match3.set(&crumb7,ast[3].clone()).unwrap();
assert_eq!(match1.get(&crumb3).unwrap(),&Ast::var(""));
assert_eq!(match1.get(&crumb4).unwrap(),&Ast::var(""));
assert_eq!(match1.get(&crumb5).unwrap(),&Ast::var(""));
assert_eq!(match1.get(&crumb6).unwrap(),&Ast::var(""));
assert_eq!(match1.get(&crumb7).unwrap(),&Ast::var(""));
assert_eq!(match1.get(&crumb3).unwrap(),&ast[0]);
assert_eq!(match1.get(&crumb4).unwrap(),&ast[0]);
assert_eq!(match1.get(&crumb5).unwrap(),&ast[0]);
assert_eq!(match1.get(&crumb6).unwrap(),&ast[0]);
assert_eq!(match1.get(&crumb7).unwrap(),&ast[0]);
assert_eq!(match4.get(&crumb3).unwrap(),&Ast::var("X"));
assert_eq!(match4.get(&crumb4).unwrap(),&Ast::var(""));
assert_eq!(match4.get(&crumb5).unwrap(),&Ast::var("Y"));
assert_eq!(match4.get(&crumb6).unwrap(),&Ast::var(""));
assert_eq!(match4.get(&crumb7).unwrap(),&Ast::var("Z"));
assert_eq!(match4.get(&crumb3).unwrap(),&ast[1]);
assert_eq!(match4.get(&crumb4).unwrap(),&ast[0]);
assert_eq!(match4.get(&crumb5).unwrap(),&ast[2]);
assert_eq!(match4.get(&crumb6).unwrap(),&ast[0]);
assert_eq!(match4.get(&crumb7).unwrap(),&ast[3]);
}

View File

@ -91,11 +91,20 @@ where for<'t> &'t Shape<Ast> : TryInto<&'t T,Error=E> {
}
impl<T:Into<Shape<Ast>>> KnownAst<T> {
/// Creates a new `KnownAst<T>` from `shape`.
/// Creates a new `KnownAst<T>` from `shape` with random ID if id=None.
pub fn new(shape:T, id:Option<crate::Id>) -> KnownAst<T> {
let ast = Ast::new(shape,id);
Self::new_unchecked(ast)
}
/// Creates a new `KnownAst<T>` from `shape` with no ID.
/// Should be only used on nodes that can't have ID because of scala AST design.
/// Example: Module, Section.opr, MacroMatchSegment.head
/// Tracking issue: https://github.com/luna/ide/issues/434
pub fn new_no_id(shape:T) -> KnownAst<T> {
let ast = Ast::new_no_id(shape);
Self::new_unchecked(ast)
}
}
impl<T,E> Deref for KnownAst<T>

View File

@ -231,28 +231,35 @@ impl Ast {
self
}
/// Wraps given shape with an optional ID into Ast.
/// Wraps given shape with ID into Ast with random ID if id=None.
/// Length will ba automatically calculated based on Shape.
/// This constructor shouldn't be used for AST that can't have ID because of scala AST design.
/// For more info see `Ast::new_no_id`
pub fn new<S:Into<Shape<Ast>>>(shape:S, id:Option<Id>) -> Ast {
let shape: Shape<Ast> = shape.into();
let shape = shape.into();
let id = id.unwrap_or_else(Id::new_v4);
let length = shape.len();
Ast::new_with_length(shape,id,length)
Ast::from_ast_id_len(shape,Some(id),length)
}
/// Wraps given shape without ID into Ast.
/// Length will ba automatically calculated based on Shape.
/// Should be only used on nodes that can't have ID because of scala AST design.
/// Example: Module, Section.opr, MacroMatchSegment.head.
/// Tracking issue: https://github.com/luna/ide/issues/434
pub fn new_no_id<S:Into<Shape<Ast>>>(shape:S) -> Ast {
let shape = shape.into();
let length = shape.len();
Ast::from_ast_id_len(shape,None,length)
}
/// Just wraps shape, id and len into Ast node.
pub fn from_ast_id_len(shape:Shape<Ast>, id:Option<Id>, len:usize) -> Ast {
fn from_ast_id_len(shape:Shape<Ast>, id:Option<Id>, len:usize) -> Ast {
let with_length = WithLength { wrapped:shape , len };
let with_id = WithID { wrapped:with_length, id };
Ast { wrapped: Rc::new(with_id) }
}
/// As `new` but sets given declared length for the shape.
pub fn new_with_length<S:Into<Shape<Ast>>>
(shape:S, id:Option<Id>, len:usize) -> Ast {
let shape = shape.into();
Self::from_ast_id_len(shape,id,len)
}
/// Iterates over all transitive child nodes (including self).
pub fn iter_recursive(&self) -> impl Iterator<Item=&Ast> {
internal::iterate_subtree(self)
@ -260,7 +267,7 @@ impl Ast {
/// Returns this AST node with ID set to given value.
pub fn with_id(&self, id:Id) -> Ast {
Ast::from_ast_id_len(self.shape().clone(), Some(id), self.len())
Ast::new(self.shape().clone(), Some(id))
}
/// Returns this AST node with a newly generated unique ID.
@ -272,11 +279,6 @@ impl Ast {
pub fn with_shape<S:Into<Shape<Ast>>>(&self, shape:S) -> Ast {
Ast::new(shape.into(),self.id)
}
/// Returns this AST node with removed ID.
pub fn without_id(&self) -> Ast {
Ast::from_ast_id_len(self.shape().clone(), None, self.len())
}
}
/// Fills `id` with `None` by default.
@ -348,7 +350,7 @@ impl<'de> Visitor<'de> for AstDeserializationVisitor {
let shape = shape.ok_or_else(|| serde::de::Error::missing_field(SHAPE))?;
let id = id.unwrap_or(None); // allow missing `id` field
let len = len.ok_or_else(|| serde::de::Error::missing_field(LENGTH))?;
Ok(Ast::new_with_length(shape,id,len))
Ok(Ast::from_ast_id_len(shape,id,len))
}
}
@ -1366,9 +1368,9 @@ mod tests {
fn ast_updating_id() {
let var = Var {name:"foo".into()};
let ast = Ast::new(var, None);
assert!(ast.id.is_none());
assert!(ast.id.is_some());
let id = Uuid::default();
let id = Id::default();
let ast = ast.with_id(id);
assert_eq!(ast.id, Some(id));
}
@ -1414,7 +1416,7 @@ mod tests {
let ident = "foo".to_string();
let v = Var{ name: ident.clone() };
let ast = Ast::from(v);
assert_eq!(ast.wrapped.id, None);
assert!(ast.wrapped.id.is_some());
assert_eq!(ast.wrapped.wrapped.len, ident.len());
}
@ -1426,7 +1428,7 @@ mod tests {
let ast_without_id = Ast::new(make_var(), None);
round_trips(&ast_without_id);
let id = Uuid::parse_str("15").ok();
let id = Id::parse_str("15").ok();
let ast_with_id = Ast::new(make_var(), id);
round_trips(&ast_with_id);
}
@ -1444,7 +1446,7 @@ mod tests {
let sample_json_text = sample_json.to_string();
let ast: Ast = serde_json::from_str(&sample_json_text).unwrap();
let expected_uuid = Uuid::parse_str(uuid_str).ok();
let expected_uuid = Id::parse_str(uuid_str).ok();
assert_eq!(ast.id, expected_uuid);
let expected_length = 3;

View File

@ -25,7 +25,7 @@ use std::path::PathBuf;
const PARSER_PATH: &str = "./pkg/scala-parser.js";
/// Commit from `enso` repository that will be used to obtain parser from.
const PARSER_COMMIT: &str = "9238c6b9c1d19760529ac63da8a9f9dec5d024c6";
const PARSER_COMMIT: &str = "81bde2858900e61fcab4138dc8881757a0787dfa";
/// Magic code that needs to be prepended to ScalaJS generated parser due to:
/// https://github.com/scala-js/scala-js/issues/3677/

View File

@ -42,7 +42,7 @@ fn to_json_single_line
impl<M:Metadata> TryFrom<&SourceFile<M>> for String {
type Error = serde_json::Error;
fn try_from(val:&SourceFile<M>) -> std::result::Result<String,Self::Error> {
fn try_from(val:&SourceFile<M>) -> std::result::Result<Self,Self::Error> {
let code = val.ast.repr();
let ids = to_json_single_line(&val.ast.id_map())?;
let meta = to_json_single_line(&val.metadata)?;

View File

@ -66,7 +66,7 @@ impl Fixture {
fn deserialize_metadata(&mut self) {
let term = ast::Module {lines: vec![ast::BlockLine {elem:None,off:0}]};
let ast = known::Module::new(term,None);
let ast = known::KnownAst::new_no_id(term);
let file = SourceFile {ast, metadata: serde_json::json!({})};
let code = String::try_from(&file).unwrap();
assert_eq!(self.parser.parse_with_metadata(code).unwrap(), file);

View File

@ -1,6 +1,7 @@
use enso_prelude::*;
use ast::Ast;
use ast::HasRepr;
use ast::IdMap;
use data::text::*;
use parser::Parser;
@ -20,33 +21,24 @@ fn web_test() {
let parser = Parser::new_or_panic();
let parse = |input:&str| {
let span = Span::from_beginning(Size::new(input.len()));
let ids = IdMap::new(vec![(span,uuid)]);
let ast = parser.parse(String::from(input), ids).unwrap().wrapped;
let parse = |input| parser.parse_with_metadata(input).unwrap();
let file = |term|
SourceFile{metadata:serde_json::json!({}), ast:ast::known::KnownAst::new_no_id(term)};
match Rc::try_unwrap(ast).unwrap().wrapped.wrapped {
ast::Shape::Module(ast) => ast,
_ => panic!("Expected module."),
}
};
let line = |term| {
ast::Module {lines: vec![ast::BlockLine {elem:term,off:0}]}
};
let app_x_y = ast::Prefix {func: Ast::var("x"), off: 3, arg: Ast::var("y")};
let var_xy = ast::Var {name:"xy".into()};
assert_eq!(parse(""), line(None));
assert_eq!(parse("xy"), line(Some(Ast::new(var_xy, Some(uuid)))));
assert_eq!(parse("x y"), line(Some(Ast::new(app_x_y, Some(uuid)))));
let app = ast::Prefix{func:Ast::var("x"), off:3, arg:Ast::var("y")};
let var = ast::Var{name:"x".into()};
let deserialize_metadata = || {
let ast = ast::known::Module::new(line(None), None);
let file = SourceFile {ast, metadata: serde_json::json!({})};
let code = String::try_from(&file).unwrap();
assert_eq!(parser.parse_with_metadata(code).unwrap(), file);
};
let ast = file(line(None));
assert_eq!(parse(String::try_from(&ast).unwrap()), ast);
deserialize_metadata()
let ast = file(line(Some(Ast::new(var,Some(uuid)))));
assert_eq!(parse(String::try_from(&ast).unwrap()), ast);
let ast = file(line(Some(Ast::new(app,Some(uuid)))));
assert_eq!(parse(String::try_from(&ast).unwrap()), ast);
}

View File

@ -274,14 +274,16 @@ mod test {
let uuid1 = Uuid::new_v4();
let uuid2 = Uuid::new_v4();
let uuid3 = Uuid::new_v4();
let uuid4 = Uuid::new_v4();
let module = "2+2";
let id_map = ast::IdMap::new(vec!
[ (Span::new(Index::new(0),Size::new(1)),uuid1.clone())
, (Span::new(Index::new(2),Size::new(1)),uuid2)
, (Span::new(Index::new(0),Size::new(3)),uuid3)
[ (Span::new(Index::new(0),Size::new(1)),uuid1)
, (Span::new(Index::new(1),Size::new(1)),uuid2)
, (Span::new(Index::new(2),Size::new(1)),uuid3)
, (Span::new(Index::new(0),Size::new(3)),uuid4)
]);
let controller = Handle::new_mock(location,module,id_map,ls,parser).unwrap();
let controller = Handle::new_mock(location,module,id_map,ls,parser).unwrap();
let mut text_notifications = controller.model.subscribe_text_notifications();
let mut graph_notifications = controller.model.subscribe_graph_notifications();
@ -289,18 +291,18 @@ mod test {
// Change code from "2+2" to "22+2"
let change = TextChange::insert(Index::new(1),"2".to_string());
controller.apply_code_change(&change).unwrap();
let expected_ast = Ast::new(ast::Module {
let expected_ast = Ast::new_no_id(ast::Module {
lines: vec![BlockLine {
elem: Some(Ast::new(ast::Infix {
larg : Ast::new(ast::Number{base:None, int:"22".to_string()}, Some(uuid1)),
loff : 0,
opr : Ast::new(ast::Opr {name:"+".to_string()}, None),
opr : Ast::new(ast::Opr {name:"+".to_string()}, Some(uuid2)),
roff : 0,
rarg : Ast::new(ast::Number{base:None, int:"2".to_string()}, Some(uuid2)),
}, Some(uuid3))),
rarg : Ast::new(ast::Number{base:None, int:"2".to_string()}, Some(uuid3)),
}, Some(uuid4))),
off: 0
}]
}, None);
});
assert_eq!(expected_ast, controller.model.ast().into());
// Check emitted notifications