mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Parser: implement import (#3627)
Based on usage; I believe this handles every case in current `.enso` files. # Important Notes - `import` is a built-in macro, so an import statement parses as a `MultiSegmentApp`. - Every `import` syntax will have a segment whose leading keyword is `import`; however `import` macros can be identified more efficiently by looking at only the first keyword. A `MultiSegmentApp` is an import if and only if its first keyword is in the set { "polyglot", "from", "import" }.
This commit is contained in:
parent
c6835d2de7
commit
796b1b5b82
@ -106,7 +106,9 @@ impl FromMeta {
|
||||
let mut fields = Vec::with_capacity(fields_.size_hint().0);
|
||||
for field in fields_ {
|
||||
let meta::Field { name, type_, hide, .. } = field;
|
||||
let name = name.to_camel_case().expect("Unimplemented: Tuples.");
|
||||
let mut name_ = meta::FieldName::from_snake_case("field");
|
||||
name_.append(name.clone());
|
||||
let name = name_.to_camel_case().expect("Unimplemented: Tuples.");
|
||||
let field = match self.primitives.get(type_) {
|
||||
Some(primitive) => Field::primitive(name, *primitive),
|
||||
None => Field::object(name, self.meta_to_java[type_], true),
|
||||
|
@ -16,12 +16,12 @@ use enso_metamodel::java::bincode::MaterializerInput;
|
||||
// generated fields in Java classes by starting from a `str -> rust::FieldId` query on Rust
|
||||
// type data, and mapping fields analogously to `rust_to_java` for types.
|
||||
const CODE_GETTER: &str = "codeRepr";
|
||||
const TREE_BEGIN: &str = "spanLeftOffsetCodeReprBegin";
|
||||
const TREE_LEN: &str = "spanLeftOffsetCodeReprLen";
|
||||
const TOKEN_BEGIN: &str = "codeReprBegin";
|
||||
const TOKEN_LEN: &str = "codeReprLen";
|
||||
const TOKEN_OFFSET_BEGIN: &str = "leftOffsetCodeReprBegin";
|
||||
//const TOKEN_OFFSET_LEN: &str = "leftOffsetCodeReprLen";
|
||||
const TREE_BEGIN: &str = "fieldSpanLeftOffsetCodeReprBegin";
|
||||
const TREE_LEN: &str = "fieldSpanLeftOffsetCodeReprLen";
|
||||
const TOKEN_BEGIN: &str = "fieldCodeReprBegin";
|
||||
const TOKEN_LEN: &str = "fieldCodeReprLen";
|
||||
const TOKEN_OFFSET_BEGIN: &str = "fieldLeftOffsetCodeReprBegin";
|
||||
//const TOKEN_OFFSET_LEN: &str = "fieldLeftOffsetCodeReprLen";
|
||||
|
||||
/// Derive deserialization for all types in the typegraph.
|
||||
pub fn derive(graph: &mut TypeGraph, tree: ClassId, token: ClassId, unsupported: ClassId) {
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use crate::macros::pattern::*;
|
||||
use crate::macros::*;
|
||||
|
||||
use crate::syntax::operator;
|
||||
|
||||
|
||||
@ -14,13 +13,62 @@ use crate::syntax::operator;
|
||||
/// All built-in macro definitions.
|
||||
pub fn all() -> resolver::SegmentMap<'static> {
|
||||
let mut macro_map = resolver::SegmentMap::default();
|
||||
// macro_map.register(if_then());
|
||||
// macro_map.register(if_then_else());
|
||||
macro_map.register(if_then());
|
||||
macro_map.register(if_then_else());
|
||||
register_import_macros(&mut macro_map);
|
||||
macro_map.register(group());
|
||||
macro_map.register(type_def());
|
||||
macro_map
|
||||
}
|
||||
|
||||
fn register_import_macros(macros: &mut resolver::SegmentMap<'_>) {
|
||||
use crate::macro_definition;
|
||||
let defs = [
|
||||
macro_definition! {("import", everything()) import_body},
|
||||
macro_definition! {("import", everything(), "as", everything()) import_body},
|
||||
macro_definition! {("import", everything(), "hiding", everything()) import_body},
|
||||
macro_definition! {("polyglot", everything(), "import", everything()) import_body},
|
||||
macro_definition! {
|
||||
("polyglot", everything(), "import", everything(), "as", everything()) import_body},
|
||||
macro_definition! {
|
||||
("polyglot", everything(), "import", everything(), "hiding", everything()) import_body},
|
||||
macro_definition! {
|
||||
("from", everything(), "import", everything(), "hiding", everything()) import_body},
|
||||
macro_definition! {
|
||||
("from", everything(), "as", everything(), "import", everything()) import_body},
|
||||
macro_definition! {("from", everything(), "import", everything()) import_body},
|
||||
];
|
||||
for def in defs {
|
||||
macros.register(def);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_body(segments: NonEmptyVec<MatchedSegment>) -> syntax::Tree {
|
||||
use operator::resolve_operator_precedence_if_non_empty;
|
||||
let mut polyglot = None;
|
||||
let mut from = None;
|
||||
let mut from_as = None;
|
||||
let mut import = None;
|
||||
let mut import_as = None;
|
||||
let mut hiding = None;
|
||||
for segment in segments {
|
||||
let header = segment.header;
|
||||
let body = resolve_operator_precedence_if_non_empty(segment.result.tokens());
|
||||
let field = match header.code.as_ref() {
|
||||
"polyglot" => &mut polyglot,
|
||||
"from" => &mut from,
|
||||
"as" if import.is_none() => &mut from_as,
|
||||
"import" => &mut import,
|
||||
"as" => &mut import_as,
|
||||
"hiding" => &mut hiding,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
*field = Some(syntax::tree::MultiSegmentAppSegment { header, body });
|
||||
}
|
||||
let import = import.unwrap();
|
||||
syntax::Tree::import(polyglot, from, from_as, import, import_as, hiding)
|
||||
}
|
||||
|
||||
/// If-then-else macro definition.
|
||||
pub fn if_then_else<'s>() -> Definition<'s> {
|
||||
crate::macro_definition! {("if", everything(), "then", everything(), "else", everything())}
|
||||
@ -33,7 +81,25 @@ pub fn if_then<'s>() -> Definition<'s> {
|
||||
|
||||
/// Group macro definition.
|
||||
pub fn group<'s>() -> Definition<'s> {
|
||||
crate::macro_definition! {("(", everything(), ")", nothing())}
|
||||
crate::macro_definition! {("(", everything(), ")", nothing()) group_body}
|
||||
}
|
||||
|
||||
fn group_body(segments: NonEmptyVec<MatchedSegment>) -> syntax::Tree {
|
||||
use operator::resolve_operator_precedence_if_non_empty;
|
||||
use syntax::token;
|
||||
macro_rules! into_symbol {
|
||||
($token:expr) => {{
|
||||
let token::Token { left_offset, code, .. } = $token;
|
||||
token::symbol(left_offset, code)
|
||||
}};
|
||||
}
|
||||
let (close, mut segments) = segments.pop();
|
||||
let close = into_symbol!(close.header);
|
||||
let segment = segments.pop().unwrap();
|
||||
let open = into_symbol!(segment.header);
|
||||
let body = segment.result.tokens();
|
||||
let body = resolve_operator_precedence_if_non_empty(body);
|
||||
syntax::Tree::group(open, body, close)
|
||||
}
|
||||
|
||||
/// New type definition macro definition.
|
||||
|
@ -71,6 +71,7 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
|
||||
/// [`Tree`] variants definition. See its docs to learn more.
|
||||
#[tagged_enum]
|
||||
#[derive(Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
||||
#[allow(clippy::large_enum_variant)] // Inefficient. Will be fixed in #182878443.
|
||||
#[tagged_enum(apply_attributes_to = "variants")]
|
||||
#[reflect(inline)]
|
||||
pub enum Variant<'s> {
|
||||
@ -193,9 +194,25 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
|
||||
/// It is an error for this to be empty.
|
||||
pub body: Option<Tree<'s>>,
|
||||
},
|
||||
/// An import statement.
|
||||
Import {
|
||||
pub polyglot: Option<MultiSegmentAppSegment<'s>>,
|
||||
pub from: Option<MultiSegmentAppSegment<'s>>,
|
||||
pub from_as: Option<MultiSegmentAppSegment<'s>>,
|
||||
pub import: MultiSegmentAppSegment<'s>,
|
||||
pub import_as: Option<MultiSegmentAppSegment<'s>>,
|
||||
pub hiding: Option<MultiSegmentAppSegment<'s>>,
|
||||
},
|
||||
/// An expression grouped by matched parentheses.
|
||||
Group {
|
||||
pub open: token::Symbol<'s>,
|
||||
pub body: Option<Tree<'s>>,
|
||||
pub close: token::Symbol<'s>,
|
||||
}
|
||||
}
|
||||
}};}
|
||||
|
||||
|
||||
macro_rules! generate_variant_constructors {
|
||||
(
|
||||
$(#$enum_meta:tt)*
|
||||
|
@ -49,9 +49,7 @@ fn application() {
|
||||
|
||||
#[test]
|
||||
fn parentheses_simple() {
|
||||
let expected = block![
|
||||
(MultiSegmentApp #(((Symbol "(") (App (Ident a) (Ident b))) ((Symbol ")") ())))
|
||||
];
|
||||
let expected = block![(Group "(" (App (Ident a) (Ident b)) ")")];
|
||||
test("(a b)", expected);
|
||||
}
|
||||
|
||||
@ -67,12 +65,11 @@ fn section_simple() {
|
||||
fn parentheses_nested() {
|
||||
#[rustfmt::skip]
|
||||
let expected = block![
|
||||
(MultiSegmentApp #(
|
||||
((Symbol "(")
|
||||
(App (MultiSegmentApp #(((Symbol "(") (App (Ident a) (Ident b))) ((Symbol ")") ())))
|
||||
(Ident c)))
|
||||
((Symbol ")") ())))
|
||||
];
|
||||
(Group
|
||||
"("
|
||||
(App (Group "(" (App (Ident a) (Ident b)) ")")
|
||||
(Ident c))
|
||||
")")];
|
||||
test("((a b) c)", expected);
|
||||
}
|
||||
|
||||
@ -151,6 +148,23 @@ fn type_def_full() {
|
||||
test(&code.join("\n"), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_def_nested() {
|
||||
#[rustfmt::skip]
|
||||
let code = [
|
||||
"type Foo",
|
||||
" type Bar",
|
||||
" type Baz",
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
let expected = block![
|
||||
(TypeDef (Ident type) (Ident Foo) #() #()
|
||||
#((TypeDef (Ident type) (Ident Bar) #() #() #())
|
||||
(TypeDef (Ident type) (Ident Baz) #() #() #())))
|
||||
];
|
||||
test(&code.join("\n"), expected);
|
||||
}
|
||||
|
||||
|
||||
// === Variable Assignment ===
|
||||
|
||||
@ -362,6 +376,61 @@ fn plus_negative() {
|
||||
}
|
||||
|
||||
|
||||
// === Import ===
|
||||
|
||||
#[test]
|
||||
fn import() {
|
||||
#[rustfmt::skip]
|
||||
let cases = [
|
||||
("import project.IO", block![
|
||||
(Import () () () ((Ident import) (OprApp (Ident project) (Ok ".") (Ident IO))) () ())]),
|
||||
("import Standard.Base as Enso_List", block![
|
||||
(Import () () ()
|
||||
((Ident import) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
||||
((Ident as) (Ident Enso_List))
|
||||
())]),
|
||||
("from Standard.Base import all", block![
|
||||
(Import ()
|
||||
((Ident from) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
||||
()
|
||||
((Ident import) (Ident all))
|
||||
() ())]),
|
||||
("from Standard.Base import all hiding Number, Boolean", block![
|
||||
(Import ()
|
||||
((Ident from) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
||||
()
|
||||
((Ident import) (Ident all))
|
||||
()
|
||||
((Ident hiding)
|
||||
(App (OprSectionBoundary (OprApp (Ident Number) (Ok ",") ())) (Ident Boolean))))]),
|
||||
("from Standard.Table as Column_Module import Column", block![
|
||||
(Import ()
|
||||
((Ident from) (OprApp (Ident Standard) (Ok ".") (Ident Table)))
|
||||
((Ident as) (Ident Column_Module))
|
||||
((Ident import) (Ident Column))
|
||||
() ())]),
|
||||
("polyglot java import java.lang.Float", block![
|
||||
(Import
|
||||
((Ident polyglot) (Ident java))
|
||||
()
|
||||
()
|
||||
((Ident import)
|
||||
(OprApp (OprApp (Ident java) (Ok ".") (Ident lang)) (Ok ".") (Ident Float)))
|
||||
() ())]),
|
||||
("polyglot java import java.net.URI as Java_URI", block![
|
||||
(Import
|
||||
((Ident polyglot) (Ident java))
|
||||
()
|
||||
()
|
||||
((Ident import)
|
||||
(OprApp (OprApp (Ident java) (Ok ".") (Ident net)) (Ok ".") (Ident URI)))
|
||||
((Ident as) (Ident Java_URI))
|
||||
())]),
|
||||
];
|
||||
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====================
|
||||
// === Test Support ===
|
||||
|
Loading…
Reference in New Issue
Block a user