mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 19:21:54 +03:00
Implement annotations (#3780)
- `->` lambda operator isn't bound by nospace groups; see new test case. - Implemented annotations.
This commit is contained in:
parent
ea60cd5fab
commit
2fab9ee1e9
@ -629,6 +629,12 @@ fn analyze_operator(token: &str) -> token::OperatorProperties {
|
||||
.with_unary_prefix_mode(token::Precedence::max())
|
||||
.as_compile_time_operation()
|
||||
.as_suspension(),
|
||||
"@" =>
|
||||
return operator
|
||||
.with_unary_prefix_mode(token::Precedence::max())
|
||||
.with_binary_infix_precedence(20)
|
||||
.as_compile_time_operation()
|
||||
.as_annotation(),
|
||||
"-" =>
|
||||
return operator
|
||||
.with_unary_prefix_mode(token::Precedence::max())
|
||||
@ -666,7 +672,6 @@ fn analyze_operator(token: &str) -> token::OperatorProperties {
|
||||
.with_binary_infix_precedence(1)
|
||||
.as_compile_time_operation()
|
||||
.as_sequence(),
|
||||
"@" => return operator.with_binary_infix_precedence(20).as_compile_time_operation(),
|
||||
"." => return operator.with_binary_infix_precedence(21).with_decimal_interpretation(),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ pub fn all() -> resolver::SegmentMap<'static> {
|
||||
macro_map.register(array());
|
||||
macro_map.register(tuple());
|
||||
macro_map.register(splice());
|
||||
|
||||
macro_map
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,8 @@ impl<'s> ExpressionBuilder<'s> {
|
||||
// it's acting as unary.
|
||||
(true, _, Some(prec)) => self.push_operator(prec, assoc, Arity::Unary(opr)),
|
||||
// Outside of a nospace group, a unary-only operator is missing an operand.
|
||||
(false, None, Some(_)) => self.operand(syntax::Tree::unary_opr_app(opr, None).into()),
|
||||
(false, None, Some(_)) =>
|
||||
self.operand(syntax::tree::apply_unary_operator(opr, None).into()),
|
||||
// Binary operator section (no LHS).
|
||||
(_, Some(prec), _) => self.binary_operator(prec, assoc, opr),
|
||||
// Failed to compute a role for the operator; this should not be possible.
|
||||
@ -238,7 +239,7 @@ impl<'s> ExpressionBuilder<'s> {
|
||||
let rhs_ = rhs.take();
|
||||
let ast = match opr.opr {
|
||||
Arity::Unary(opr) =>
|
||||
Operand::from(rhs_).map(|item| syntax::Tree::unary_opr_app(opr, item)),
|
||||
Operand::from(rhs_).map(|item| syntax::tree::apply_unary_operator(opr, item)),
|
||||
Arity::Binary { tokens, lhs_section_termination } => {
|
||||
let lhs = self.output.pop();
|
||||
if let Some(lhs_termination) = lhs_section_termination {
|
||||
|
@ -337,6 +337,7 @@ pub struct OperatorProperties {
|
||||
is_arrow: bool,
|
||||
is_sequence: bool,
|
||||
is_suspension: bool,
|
||||
is_annotation: bool,
|
||||
}
|
||||
|
||||
impl OperatorProperties {
|
||||
@ -392,6 +393,11 @@ impl OperatorProperties {
|
||||
Self { is_sequence: true, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this operator, modified to be flagged as the annotation operator.
|
||||
pub fn as_annotation(self) -> Self {
|
||||
Self { is_annotation: true, ..self }
|
||||
}
|
||||
|
||||
/// Return a copy of this operator, modified to be flagged as the execution-suspension operator.
|
||||
pub fn as_suspension(self) -> Self {
|
||||
Self { is_suspension: true, ..self }
|
||||
@ -447,6 +453,11 @@ impl OperatorProperties {
|
||||
self.is_suspension
|
||||
}
|
||||
|
||||
/// Return whether this operator is the annotation operator.
|
||||
pub fn is_annotation(&self) -> bool {
|
||||
self.is_annotation
|
||||
}
|
||||
|
||||
/// Return this operator's associativity.
|
||||
pub fn associativity(&self) -> Associativity {
|
||||
match self.is_right_associative {
|
||||
|
@ -299,6 +299,13 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
|
||||
pub rest: Vec<OperatorDelimitedTree<'s>>,
|
||||
pub right: token::CloseSymbol<'s>,
|
||||
},
|
||||
/// An expression preceded by an annotation, e.g. `@Builtin_Method foo`.
|
||||
Annotated {
|
||||
pub token: token::Operator<'s>,
|
||||
pub annotation: token::Ident<'s>,
|
||||
pub newlines: Vec<token::Newline<'s>>,
|
||||
pub expression: Option<Tree<'s>>,
|
||||
},
|
||||
}
|
||||
}};}
|
||||
|
||||
@ -735,6 +742,14 @@ pub fn apply<'s>(mut func: Tree<'s>, mut arg: Tree<'s>) -> Tree<'s> {
|
||||
func_.fractional_digits = mem::take(fractional_digits);
|
||||
return func;
|
||||
}
|
||||
Variant::Annotated(func_ @ Annotated { expression: None, .. }) => {
|
||||
func_.expression = arg.into();
|
||||
return func;
|
||||
}
|
||||
Variant::Annotated(Annotated { expression: Some(expression), .. }) => {
|
||||
*expression = apply(mem::take(expression), arg);
|
||||
return func;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
match &mut *arg.variant {
|
||||
@ -876,6 +891,18 @@ pub fn apply_operator<'s>(
|
||||
Tree::opr_app(lhs, opr, rhs)
|
||||
}
|
||||
|
||||
/// Apply a unary operator to an operand.
|
||||
///
|
||||
/// For most inputs this will simply construct a `UnaryOprApp`; however, some operators are special.
|
||||
pub fn apply_unary_operator<'s>(opr: token::Operator<'s>, rhs: Option<Tree<'s>>) -> Tree<'s> {
|
||||
if opr.properties.is_annotation()
|
||||
&& let Some(Tree { variant: box Variant::Ident(Ident { token }), .. }) = rhs {
|
||||
Tree::annotated(opr, token, vec![], None)
|
||||
} else {
|
||||
Tree::unary_opr_app(opr, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<Token<'s>> for Tree<'s> {
|
||||
fn from(token: Token<'s>) -> Self {
|
||||
match token.variant {
|
||||
@ -957,6 +984,7 @@ pub fn recurse_left_mut_while<'s>(
|
||||
| Variant::TypeSignature(_)
|
||||
| Variant::Lambda(_)
|
||||
| Variant::Array(_)
|
||||
| Variant::Annotated(_)
|
||||
| Variant::Tuple(_) => break,
|
||||
// Optional LHS.
|
||||
Variant::ArgumentBlockApplication(ArgumentBlockApplication { lhs, .. })
|
||||
|
@ -50,11 +50,25 @@ impl<'s> span::Builder<'s> for Line<'s> {
|
||||
/// Build a body block from a sequence of lines; this involves reinterpreting the input expressions
|
||||
/// in statement context (i.e. expressions at the top-level of the block that involve the `=`
|
||||
/// operator will be reinterpreted as function/variable bindings).
|
||||
pub fn body_from_lines<'s>(expressions: impl IntoIterator<Item = Line<'s>>) -> Tree<'s> {
|
||||
pub fn body_from_lines<'s>(lines: impl IntoIterator<Item = Line<'s>>) -> Tree<'s> {
|
||||
use crate::expression_to_statement;
|
||||
let expressions = expressions.into_iter();
|
||||
let statements = expressions.map(|line| line.map_expression(expression_to_statement));
|
||||
let statements = statements.collect();
|
||||
let mut lines = lines.into_iter();
|
||||
let mut statements = Vec::with_capacity(lines.size_hint().0);
|
||||
while let Some(line) = lines.next() {
|
||||
let mut statement = line.map_expression(expression_to_statement);
|
||||
if let Some(Tree {
|
||||
variant: box Variant::Annotated(Annotated { newlines, expression, .. }),
|
||||
..
|
||||
}) = &mut statement.expression
|
||||
{
|
||||
while expression.is_none() && let Some(line) = lines.next() {
|
||||
let statement = line.map_expression(expression_to_statement);
|
||||
newlines.push(statement.newline);
|
||||
*expression = statement.expression;
|
||||
}
|
||||
}
|
||||
statements.push(statement);
|
||||
}
|
||||
Tree::body_block(statements)
|
||||
}
|
||||
|
||||
|
@ -1020,6 +1020,42 @@ fn trailing_whitespace() {
|
||||
}
|
||||
|
||||
|
||||
// === Annotations ===
|
||||
|
||||
#[test]
|
||||
fn annotation_syntax() {
|
||||
#[rustfmt::skip]
|
||||
let cases = [
|
||||
("foo@bar", block![(OprApp (Ident foo) (Ok "@") (Ident bar))]),
|
||||
("foo @ bar", block![(OprApp (Ident foo) (Ok "@") (Ident bar))]),
|
||||
("@Bar", block![(Annotated "@" Bar #() ())]),
|
||||
];
|
||||
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_annotations() {
|
||||
#[rustfmt::skip]
|
||||
let cases = [
|
||||
("@Tail_Call go t", block![(Annotated "@" Tail_Call #() (App (Ident go) (Ident t)))]),
|
||||
("@Tail_Call go\n a\n b", block![
|
||||
(Annotated "@" Tail_Call #()
|
||||
(ArgumentBlockApplication (Ident go) #((Ident a) (Ident b))))]),
|
||||
];
|
||||
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_annotations() {
|
||||
#[rustfmt::skip]
|
||||
let cases = [
|
||||
("@Builtin_Type\ntype Date", block![
|
||||
(Annotated "@" Builtin_Type #(()) (TypeDef type Date #() #() #()))]),
|
||||
];
|
||||
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====================
|
||||
// === Test Support ===
|
||||
|
Loading…
Reference in New Issue
Block a user