Macro contexts (#3792)

- Implement macro-contexts-lite (`from` is now only a keyword at the beginning of a line)
- Support special nospace-group handling for old lambdas (so expressions like this work: `x-> y-> x + y`)
- Fix a text-escape incompatibility

# Important Notes
- There is now an `OperatorFunction`, which is like a `Function` but has an operator for a name, and likewise an `OperatorTypeSignature`.
This commit is contained in:
Kaz Wesley 2022-10-13 15:47:02 -07:00 committed by GitHub
parent e188b15973
commit 0e412044f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 267 additions and 149 deletions

View File

@ -456,43 +456,22 @@ final class TreeToIr {
// }
yield null;
}
case Tree.OperatorTypeSignature sig -> {
var typeName = buildName(getIdentifiedLocation(sig.getOperator()), sig.getOperator(), true);
yield translateTypeSignature(sig, sig.getType(), typeName);
}
case Tree.TypeSignature sig -> {
var typeName = buildName(sig.getVariable());
yield translateTypeSignature(sig, sig.getType(), typeName);
}
var fn = switch (sig.getType()) {
case Tree.OprApp app when "->".equals(app.getOpr().getRight().codeRepr()) -> {
var args = cons(translateExpression(app.getLhs(), true), nil());
var rhs = app.getRhs();
while (rhs instanceof Tree.OprApp at && "->".equals(at.getOpr().getRight().codeRepr())) {
args = cons(translateExpression(at.getLhs(), true), args);
rhs = at.getRhs();
}
var ret = translateExpression(rhs, true);
yield new IR$Type$Function(
args,
ret,
Option.empty(),
meta(), diag()
);
}
case Tree.OprApp app -> {
yield translateExpression(app, true);
}
case Tree.Ident ident -> {
yield buildName(ident);
}
default -> throw new UnhandledEntity(sig.getType(), "translateTypeBodyExpression");
};
yield new IR$Type$Ascription(typeName, fn, getIdentifiedLocation(sig), meta(), diag());
case Tree.OperatorFunction fun -> {
var name = buildName(getIdentifiedLocation(fun.getName()), fun.getName(), true);
yield translateFunction(fun, name, fun.getArgs(), fun.getBody());
}
case Tree.Function fun -> {
var name = buildName(fun.getName());
var args = translateArgumentsDefinition(fun.getArgs());
var body = translateExpression(fun.getBody(), false);
yield new IR$Function$Binding(name, args, body,
getIdentifiedLocation(fun), true, meta(), diag()
);
yield translateFunction(fun, name, fun.getArgs(), fun.getBody());
}
/*
case AstView.FunctionSugar(
@ -545,6 +524,42 @@ final class TreeToIr {
};
}
private IR$Type$Ascription translateTypeSignature(Tree sig, Tree type, IR.Name typeName) throws UnhandledEntity {
var fn = switch (type) {
case Tree.OprApp app when "->".equals(app.getOpr().getRight().codeRepr()) -> {
var args = cons(translateExpression(app.getLhs(), true), nil());
var rhs = app.getRhs();
while (rhs instanceof Tree.OprApp at && "->".equals(at.getOpr().getRight().codeRepr())) {
args = cons(translateExpression(at.getLhs(), true), args);
rhs = at.getRhs();
}
var ret = translateExpression(rhs, true);
yield new IR$Type$Function(
args,
ret,
Option.empty(),
meta(), diag()
);
}
case Tree.OprApp app -> {
yield translateExpression(app, true);
}
case Tree.Ident ident -> {
yield buildName(ident);
}
default -> throw new UnhandledEntity(type, "translateTypeBodyExpression");
};
return new IR$Type$Ascription(typeName, fn, getIdentifiedLocation(sig), meta(), diag());
}
private IR$Function$Binding translateFunction(Tree fun, IR.Name name, java.util.List<ArgumentDefinition> arguments, final Tree treeBody) {
var args = translateArgumentsDefinition(arguments);
var body = translateExpression(treeBody, false);
return new IR$Function$Binding(name, args, body,
getIdentifiedLocation(fun), true, meta(), diag()
);
}
/*
private def translateForeignDefinition(header: List[AST], body: AST): Either[
String,
@ -858,7 +873,7 @@ final class TreeToIr {
getIdentifiedLocation(sig),
meta(), diag()
);
var opName = buildName(null, sig.getOperator(), true);
var opName = buildName(Option.empty(), sig.getOperator(), true);
var signature = translateCallArgument(sig.getType(), true);
yield new IR$Application$Operator$Binary(methodReference, opName, signature, getIdentifiedLocation(sig), meta(), diag());
}
@ -1472,7 +1487,7 @@ final class TreeToIr {
IR.Expression translateIdent(Tree identifier, boolean isMethod) {
return switch (identifier) {
case null -> null;
case Tree.Ident id -> sanitizeName(buildName(id, id.getToken(), isMethod));
case Tree.Ident id -> sanitizeName(buildName(getIdentifiedLocation(id), id.getToken(), isMethod));
default -> throw new UnhandledEntity(identifier, "translateIdent");
};
/*
@ -1814,39 +1829,22 @@ final class TreeToIr {
}
private IR$Name$Literal buildName(Token name) {
return new IR$Name$Literal(
name.codeRepr(),
false,
getIdentifiedLocation(name),
meta(), diag()
);
return buildName(getIdentifiedLocation(name), name, false);
}
private IR$Name$Literal buildName(Tree ident) {
return switch (ident) {
case Tree.Ident id -> buildName(ident, id.getToken(), false);
case Tree.Ident id -> buildName(getIdentifiedLocation(ident), id.getToken(), false);
default -> throw new UnhandledEntity(ident, "buildName");
};
}
private IR$Name$Literal buildName(Tree ident, Token id) {
return buildName(ident, id, false);
return buildName(getIdentifiedLocation(ident), id, false);
}
private IR$Name$Literal buildName(Tree ident, Token id, boolean isMethod) {
private IR$Name$Literal buildName(Option<IdentifiedLocation> loc, Token id, boolean isMethod) {
final String name = id.codeRepr();
// AST.Opr.any.unapply(ident).isDefined
NOT_OPERATOR: if (!isMethod) {
for (int i = 0; i < name.length(); i++) {
if (Character.isJavaIdentifierPart(name.charAt(i))) {
break NOT_OPERATOR;
}
}
isMethod = true;
}
return new IR$Name$Literal(
name,
isMethod,
getIdentifiedLocation(ident),
meta(), diag()
);
return new IR$Name$Literal(name, isMethod, loc, meta(), diag());
}
private IR.Name sanitizeName(IR$Name$Literal id) {

View File

@ -164,10 +164,10 @@ fn type_operator_methods() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #() #()
#((TypeSignature (Ident #"+") ":"
#((OperatorTypeSignature "+" ":"
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))
(Function (Ident #"+") #((() (Ident self) () ()) (() (Ident b) () ()))
"=" (Ident b))))];
(OperatorFunction "+" #((() (Ident self) () ()) (() (Ident b) () ()))
"=" (Ident b))))];
test(&code.join("\n"), expected);
}
@ -368,7 +368,6 @@ fn code_block_operator() {
}
#[test]
//#[ignore] // WIP
fn dot_operator_blocks() {
let code = ["rect1", " . width = 7", " . center", " + x"];
#[rustfmt::skip]
@ -798,9 +797,10 @@ fn inline_text_literals() {
(Assignment (Ident unclosed) "=" (TextLiteral #((Section "a"))))]),
(r#"'Other quote type'"#, block![(TextLiteral #((Section "Other quote type")))]),
(r#""Non-escape: \n""#, block![(TextLiteral #((Section "Non-escape: \\n")))]),
(r#""String with \" escape""#, block![
(r#""Non-escape: \""#, block![(TextLiteral #((Section "Non-escape: \\")))]),
(r#"'String with \' escape'"#, block![
(TextLiteral
#((Section "String with ") (Escape '\"') (Section " escape")))]),
#((Section "String with ") (Escape '\'') (Section " escape")))]),
(r#"'\u0915\u094D\u0937\u093F'"#, block![(TextLiteral #(
(Escape '\u{0915}') (Escape '\u{094D}') (Escape '\u{0937}') (Escape '\u{093F}')))]),
(r#"('\n')"#, block![(Group (TextLiteral #((Escape '\n'))))]),
@ -904,7 +904,13 @@ fn new_lambdas() {
#[test]
fn old_lambdas() {
let cases = [("v -> v", block![(OprApp (Ident v) (Ok "->") (Ident v))])];
let cases = [
("x -> y", block![(OprApp (Ident x) (Ok "->") (Ident y))]),
("x->y", block![(OprApp (Ident x) (Ok "->") (Ident y))]),
("x-> y", block![(OprApp (Ident x) (Ok "->") (Ident y))]),
("x->\n y", block![(OprApp (Ident x) (Ok "->") (BodyBlock #((Ident y))))]),
("x ->\n y", block![(OprApp (Ident x) (Ok "->") (BodyBlock #((Ident y))))]),
];
cases.into_iter().for_each(|(code, expected)| test(code, expected));
}

View File

@ -925,10 +925,10 @@ impl<'s> Lexer<'s> {
text_start = self.mark();
continue;
}
if char == '\\' {
if interpolate && char == '\\' {
let backslash_start = self.mark();
self.take_next();
if let Some(char) = self.current_char && (interpolate || closing_char == Some(char)) {
if let Some(char) = self.current_char {
let token = self.make_token(
text_start,
backslash_start.clone(),

View File

@ -170,7 +170,7 @@ pub mod prelude {
#[allow(missing_docs)]
#[derive(Debug)]
pub struct Parser {
pub macros: macros::resolver::SegmentMap<'static>,
pub macros: macros::resolver::MacroMap,
}
impl Parser {
@ -210,10 +210,20 @@ fn expression_to_statement(mut tree: syntax::Tree<'_>) -> syntax::Tree<'_> {
use syntax::tree::*;
let mut left_offset = source::span::Offset::default();
if let Tree { variant: box Variant::TypeAnnotated(annotated), span } = tree {
let operator = annotated.operator;
let colon = annotated.operator;
let type_ = annotated.type_;
let variable = annotated.expression;
let mut tree = Tree::type_signature(variable, operator, type_);
if let Tree {
variant: box Variant::OprApp(OprApp { lhs: None, opr: Ok(name), rhs: None }),
span: inner,
} = variable
{
let mut tree = Tree::operator_type_signature(name, colon, type_);
tree.span.left_offset += span.left_offset;
tree.span.left_offset += inner.left_offset;
return tree;
}
let mut tree = Tree::type_signature(variable, colon, type_);
tree.span.left_offset += span.left_offset;
return tree;
}
@ -226,6 +236,15 @@ fn expression_to_statement(mut tree: syntax::Tree<'_>) -> syntax::Tree<'_> {
_ => return tree,
};
if let OprApp { lhs: Some(lhs), opr: Ok(opr), rhs } = opr_app && opr.properties.is_assignment() {
if let Tree { variant: box Variant::OprApp(
OprApp { lhs: None, opr: Ok(name), rhs: Some(args) }), span } = lhs {
let args = collect_arguments_inclusive(mem::take(args));
let name = mem::take(name);
let mut result = Tree::operator_function(name, args, mem::take(opr), mem::take(rhs));
result.span.left_offset += mem::take(&mut span.left_offset);
result.span.left_offset += left_offset;
return result;
}
let (leftmost, args) = collect_arguments(lhs.clone());
if let Some(rhs) = rhs {
if let Variant::Ident(ident) = &*leftmost.variant && ident.token.variant.is_type {
@ -305,42 +324,29 @@ fn expression_to_pattern(mut input: syntax::Tree<'_>) -> syntax::Tree<'_> {
out
}
fn collect_arguments(
mut tree: syntax::Tree,
) -> (syntax::Tree, Vec<syntax::tree::ArgumentDefinition>) {
if let box syntax::tree::Variant::OprApp(syntax::tree::OprApp {
lhs: None,
opr: Ok(opr),
rhs: Some(rhs),
}) = tree.variant
{
let syntax::token::Token { left_offset, code, .. } = opr;
let opr = syntax::token::ident(left_offset, code, false, 0, false, false);
let mut opr = Some(syntax::Tree::ident(opr));
let mut tree_ = rhs;
let mut left_offset = tree.span.left_offset;
syntax::tree::recurse_left_mut_while(&mut tree_, |tree| {
left_offset += mem::take(&mut tree.span.left_offset);
match &mut *tree.variant {
syntax::tree::Variant::App(syntax::tree::App { func, .. })
if !matches!(&*func.variant, syntax::tree::Variant::App(_)) =>
{
let mut func_ = func.clone();
func_.span.left_offset = mem::take(&mut left_offset);
*func = syntax::Tree::app(opr.take().unwrap(), func_);
false
}
_ => true,
}
});
tree = tree_;
}
fn collect_arguments(tree: syntax::Tree) -> (syntax::Tree, Vec<syntax::tree::ArgumentDefinition>) {
let mut args = vec![];
let tree = unroll_arguments(tree, &mut args);
args.reverse();
(tree, args)
}
fn collect_arguments_inclusive(tree: syntax::Tree) -> Vec<syntax::tree::ArgumentDefinition> {
let mut args = vec![];
let first = unroll_arguments(tree, &mut args);
args.push(parse_argument_definition(first));
args.reverse();
args
}
fn unroll_arguments<'s>(
mut tree: syntax::Tree<'s>,
args: &mut Vec<syntax::tree::ArgumentDefinition<'s>>,
) -> syntax::Tree<'s> {
while let Some(arg) = parse_argument_application(&mut tree) {
args.push(arg);
}
args.reverse();
(tree, args)
tree
}
/// Try to parse the expression as an application of a function to an `ArgumentDefinition`. If it

View File

@ -12,20 +12,30 @@ use crate::syntax::operator;
// =======================
/// All built-in macro definitions.
pub fn all() -> resolver::SegmentMap<'static> {
pub fn all() -> resolver::MacroMap {
resolver::MacroMap { expression: expression(), statement: statement() }
}
/// Built-in macro definitions that match anywhere in an expression.
fn expression() -> resolver::SegmentMap<'static> {
let mut macro_map = resolver::SegmentMap::default();
macro_map.register(if_then());
macro_map.register(if_then_else());
register_import_macros(&mut macro_map);
register_export_macros(&mut macro_map);
macro_map.register(group());
macro_map.register(type_def());
macro_map.register(lambda());
macro_map.register(case());
macro_map.register(array());
macro_map.register(tuple());
macro_map.register(splice());
macro_map
}
/// Built-in macro definitions that match only from the first token in a line.
fn statement() -> resolver::SegmentMap<'static> {
let mut macro_map = resolver::SegmentMap::default();
register_import_macros(&mut macro_map);
register_export_macros(&mut macro_map);
macro_map.register(type_def());
macro_map
}

View File

@ -36,6 +36,36 @@ use std::collections::VecDeque;
// ================
// === MacroMap ===
// ================
/// Represents the sets of macros defined in different contexts.
#[derive(Default, Debug)]
pub struct MacroMap {
/// Macros that can occur anywhere in an expression.
pub expression: SegmentMap<'static>,
/// Macros that can only occur in statement context.
pub statement: SegmentMap<'static>,
}
impl MacroMap {
/// Return the macro matching the given token in the given context, if any.
fn get(&self, key: &str, context: Context) -> Option<&NonEmptyVec<SegmentEntry<'static>>> {
let statement_result = || self.statement.get(key);
let expression_result = || self.expression.get(key);
(context == Context::Statement).then(statement_result).flatten().or_else(expression_result)
}
}
#[derive(Debug, PartialEq, Eq)]
enum Context {
Expression,
Statement,
}
// ==================
// === SegmentMap ===
// ==================
@ -267,7 +297,7 @@ impl<'s> Resolver<'s> {
/// Run the resolver. Returns the resolved AST.
pub fn run(
mut self,
root_macro_map: &SegmentMap<'s>,
root_macro_map: &MacroMap,
tokens: Vec<syntax::Item<'s>>,
) -> syntax::Tree<'s> {
let mut tokens = tokens.into_iter();
@ -319,9 +349,13 @@ impl<'s> Resolver<'s> {
self.line_contains_items = false;
continue;
}
self.line_contains_items = true;
let line_contained_items = mem::replace(&mut self.line_contains_items, true);
let context = match line_contained_items {
true => Context::Expression,
false => Context::Statement,
};
let step_result = match token {
syntax::Item::Token(token) => self.process_token(root_macro_map, token),
syntax::Item::Token(token) => self.process_token(root_macro_map, token, context),
syntax::Item::Block(tokens_) => {
let parent_tokens = mem::replace(&mut tokens, tokens_.into_iter());
let new_root = PartiallyMatchedMacro::new_root();
@ -419,7 +453,12 @@ impl<'s> Resolver<'s> {
tree
}
fn process_token(&mut self, root_macro_map: &SegmentMap<'s>, token: Token<'s>) -> Step<'s> {
fn process_token(
&mut self,
root_macro_map: &MacroMap,
token: Token<'s>,
context: Context,
) -> Step<'s> {
let repr = &**token.code;
if !token.variant.can_start_macro_segment() {
return Step::NormalToken(token.into());
@ -437,7 +476,7 @@ impl<'s> Resolver<'s> {
trace!("Next token reserved by parent macro. Resolving current macro.");
self.replace_current_with_parent_macro(parent_macro);
Step::MacroStackPop(token.into())
} else if let Some(segments) = root_macro_map.get(repr) {
} else if let Some(segments) = root_macro_map.get(repr, context) {
trace!("Starting a new nested macro resolution.");
let mut matched_macro_def = default();
let mut current_macro = PartiallyMatchedMacro {

View File

@ -147,8 +147,24 @@ impl<'s> ExpressionBuilder<'s> {
/// Extend the expression with an operand.
pub fn operand(&mut self, mut operand: Operand<syntax::Tree<'s>>) {
if self.prev_type.replace(ItemType::Ast) == Some(ItemType::Ast) {
operand =
self.output.pop().unwrap().map(|lhs| syntax::tree::apply(lhs, operand.into()));
if let syntax::tree::Variant::OprApp(
syntax::tree::OprApp { lhs: Some(_), opr: Ok(opr), rhs: None })
= &*self.output.last().unwrap().value.variant
&& opr.properties.associativity() == token::Associativity::Right
&& opr.left_offset.is_empty() {
let syntax::Tree { span, variant: box syntax::tree::Variant::OprApp(
syntax::tree::OprApp { lhs: Some(mut lhs), opr: Ok(operator), rhs: None }) }
= self.output.pop().unwrap().value
else { unreachable!() };
lhs.span.left_offset += span.left_offset;
let precedence = operator.properties.binary_infix_precedence().unwrap();
let associativity = operator.properties.associativity();
let opr = Arity::Unary(Unary::LeftCurriedBinary { lhs, operator });
self.operator_stack.push(Operator { precedence, associativity, opr });
} else {
operand =
self.output.pop().unwrap().map(|lhs| syntax::tree::apply(lhs, operand.into()));
}
}
self.output.push(operand);
}
@ -167,7 +183,7 @@ impl<'s> ExpressionBuilder<'s> {
self.binary_operator(prec, assoc, opr),
// Otherwise, if the operator is inside a nospace group, and it has a unary role,
// it's acting as unary.
(true, _, Some(prec)) => self.push_operator(prec, assoc, Arity::Unary(opr)),
(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::apply_unary_operator(opr, None).into()),
@ -194,9 +210,7 @@ impl<'s> ExpressionBuilder<'s> {
Arity::Binary { tokens, .. } => tokens.into_iter().next().unwrap(),
_ => unreachable!(),
};
let tp = token::Variant::ident(false, 0, false, false);
let prev = Token(prev.left_offset, prev.code, tp);
self.output.push(Operand::from(syntax::Tree::from(prev)));
self.output.push(Operand::from(syntax::Tree::opr_app(None, Ok(prev), None)));
} else {
tokens.push(opr);
return;
@ -238,8 +252,14 @@ impl<'s> ExpressionBuilder<'s> {
}) {
let rhs_ = rhs.take();
let ast = match opr.opr {
Arity::Unary(opr) =>
Arity::Unary(Unary::Simple(opr)) =>
Operand::from(rhs_).map(|item| syntax::tree::apply_unary_operator(opr, item)),
Arity::Unary(Unary::LeftCurriedBinary { lhs, operator }) => {
let lhs = lhs.into();
let opr = vec![operator];
let rhs = rhs_.unwrap().value.into();
syntax::tree::apply_operator(lhs, opr, rhs, self.nospace).into()
}
Arity::Binary { tokens, lhs_section_termination } => {
let lhs = self.output.pop();
if let Some(lhs_termination) = lhs_section_termination {
@ -286,7 +306,8 @@ impl<'s> ExpressionBuilder<'s> {
pub fn extend_from(&mut self, child: &mut Self) {
if child.output.is_empty() && let Some(op) = child.operator_stack.pop() {
match op.opr {
Arity::Unary(un) => self.operator(un),
Arity::Unary(Unary::Simple(un)) => self.operator(un),
Arity::Unary(Unary::LeftCurriedBinary{ .. }) => unreachable!(),
Arity::Binary { tokens, .. } => tokens.into_iter().for_each(|op| self.operator(op)),
};
child.prev_type = None;
@ -317,7 +338,7 @@ struct Operator<'s> {
/// Classifies the role of an operator.
#[derive(Debug)]
enum Arity<'s> {
Unary(token::Operator<'s>),
Unary(Unary<'s>),
Binary {
tokens: Vec<token::Operator<'s>>,
lhs_section_termination: Option<SectionTermination>,
@ -330,6 +351,16 @@ impl<'s> Arity<'s> {
let tokens = vec![tok];
Self::Binary { tokens, lhs_section_termination }
}
fn unary(tok: token::Operator<'s>) -> Self {
Self::Unary(Unary::Simple(tok))
}
}
#[derive(Debug)]
enum Unary<'s> {
Simple(token::Operator<'s>),
LeftCurriedBinary { lhs: syntax::Tree<'s>, operator: token::Operator<'s> },
}
@ -339,7 +370,9 @@ impl<'s> Arity<'s> {
#[derive(Default, Debug)]
struct Operand<T> {
value: T,
/// Number of elided operands in the subtree, potentially forming an *operator section*.
elided: u32,
/// Number of wildcards in the subtree, potentially forming a *template function*.
wildcards: u32,
}

View File

@ -1,3 +1,4 @@
//! A lexical token is a string with an assigned and thus identified meaning. Each token remembers
//! its source code and can be printed back. It also contains information about the offset to the
//! previous token if any.
//!
@ -262,14 +263,14 @@ macro_rules! with_token_definition { ($f:ident ($($args:tt)*)) => { $f! { $($arg
},
AutoScope,
Ident {
pub is_free: bool,
pub lift_level: usize,
pub is_free: bool,
pub lift_level: usize,
#[serde(skip)]
#[reflect(skip)]
pub is_type: bool,
pub is_type: bool,
#[serde(skip)]
#[reflect(skip)]
pub is_default: bool,
pub is_default: bool,
},
Operator {
#[serde(skip)]
@ -330,8 +331,8 @@ pub struct OperatorProperties {
// Special properties
is_compile_time_operation: bool,
is_right_associative: bool,
can_be_decimal_operator: bool,
// Unique operators
can_be_decimal_operator: bool,
is_type_annotation: bool,
is_assignment: bool,
is_arrow: bool,

View File

@ -227,6 +227,18 @@ 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 operator definition, like `== self rhs = True`.
OperatorFunction {
/// The operator being defined.
pub name: token::Operator<'s>,
/// The argument patterns.
pub args: Vec<ArgumentDefinition<'s>>,
/// The `=` token.
pub equals: token::Operator<'s>,
/// The body, which will typically be an inline expression or a `BodyBlock` expression.
/// It is an error for this to be empty.
pub body: Option<Tree<'s>>,
},
/// An import statement.
Import {
pub polyglot: Option<MultiSegmentAppSegment<'s>>,
@ -262,6 +274,16 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
#[reflect(rename = "type")]
pub type_: Tree<'s>,
},
/// Statement declaring the type of an operator.
OperatorTypeSignature {
/// Operator whose type is being declared.
pub operator: token::Operator<'s>,
/// The `:` token.
pub colon: token::Operator<'s>,
/// The method's type.
#[reflect(rename = "type")]
pub type_: Tree<'s>,
},
/// An expression with explicit type information attached.
TypeAnnotated {
/// The expression whose type is being annotated.
@ -713,32 +735,32 @@ impl<'s> span::Builder<'s> for OperatorDelimitedTree<'s> {
/// For most input types, this simply constructs an `App`; however, for some operand types
/// application has special semantics.
pub fn apply<'s>(mut func: Tree<'s>, mut arg: Tree<'s>) -> Tree<'s> {
match &mut *func.variant {
Variant::TextLiteral(lhs) if lhs.close.is_none()
&& let Tree { variant: box Variant::TextLiteral(rhs), span } = arg => {
join_text_literals(lhs, rhs, span);
return func;
match (&mut *func.variant, &mut *arg.variant) {
(Variant::TextLiteral(lhs), Variant::TextLiteral(rhs)) if lhs.close.is_none() => {
join_text_literals(lhs, rhs.clone(), mem::take(&mut arg.span));
func
}
Variant::Number(func_ @ Number { base: _, integer: None, fractional_digits: None })
if let box
Variant::Number(Number { base: None, integer, fractional_digits }) = &mut arg.variant
=> {
(Variant::Number(func_ @ Number { base: _, integer: None, fractional_digits: None }),
Variant::Number(Number { base: None, integer, fractional_digits })) => {
func_.integer = mem::take(integer);
func_.fractional_digits = mem::take(fractional_digits);
return func;
func
}
Variant::Annotated(func_ @ Annotated { expression: None, .. }) => {
(Variant::Annotated(func_ @ Annotated { expression: None, .. }), _) => {
func_.expression = arg.into();
return func;
func
}
Variant::Annotated(Annotated { expression: Some(expression), .. }) => {
(Variant::Annotated(Annotated { expression: Some(expression), .. }), _) => {
*expression = apply(mem::take(expression), arg);
return func;
func
}
_ => (),
}
match &mut *arg.variant {
Variant::ArgumentBlockApplication(block) if block.lhs.is_none() => {
(Variant::OprApp(OprApp { lhs: Some(_), opr: Ok(_), rhs }),
Variant::ArgumentBlockApplication(ArgumentBlockApplication { lhs: None, arguments }))
if rhs.is_none() => {
*rhs = block::body_from_lines(mem::take(arguments)).into();
func
}
(_, Variant::ArgumentBlockApplication(block)) if block.lhs.is_none() => {
let func_left_offset = mem::take(&mut func.span.left_offset);
let arg_left_offset = mem::replace(&mut arg.span.left_offset, func_left_offset);
if let Some(first) = block.arguments.first_mut() {
@ -747,7 +769,7 @@ pub fn apply<'s>(mut func: Tree<'s>, mut arg: Tree<'s>) -> Tree<'s> {
block.lhs = Some(func);
arg
}
Variant::OperatorBlockApplication(block) if block.lhs.is_none() => {
(_, Variant::OperatorBlockApplication(block)) if block.lhs.is_none() => {
let func_left_offset = mem::take(&mut func.span.left_offset);
let arg_left_offset = mem::replace(&mut arg.span.left_offset, func_left_offset);
if let Some(first) = block.expressions.first_mut() {
@ -756,22 +778,23 @@ pub fn apply<'s>(mut func: Tree<'s>, mut arg: Tree<'s>) -> Tree<'s> {
block.lhs = Some(func);
arg
}
Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) })
if opr.properties.is_assignment() && let Variant::Ident(lhs) = &*lhs.variant => {
(_, Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) }))
if opr.properties.is_assignment() && let Variant::Ident(lhs) = &*lhs.variant => {
let mut lhs = lhs.token.clone();
lhs.left_offset += arg.span.left_offset.clone();
Tree::named_app(func, None, lhs, opr.clone(), rhs.clone(), None)
}
Variant::Group(Group { open: Some(open), body: Some(body), close: Some(close) }) if let box
Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) }) = &body.variant
&& opr.properties.is_assignment() && let Variant::Ident(lhs) = &*lhs.variant => {
(_, Variant::Group(Group { open: Some(open), body: Some(body), close: Some(close) }))
if let box Variant::OprApp(OprApp { lhs: Some(lhs), opr: Ok(opr), rhs: Some(rhs) })
= &body.variant
&& opr.properties.is_assignment() && let Variant::Ident(lhs) = &*lhs.variant => {
let mut open = open.clone();
open.left_offset += arg.span.left_offset.clone();
let open = Some(open);
let close = Some(close.clone());
Tree::named_app(func, open, lhs.token.clone(), opr.clone(), rhs.clone(), close)
}
Variant::Ident(Ident { token }) if token.is_default => {
(_, Variant::Ident(Ident { token })) if token.is_default => {
let mut token = token.clone();
token.left_offset += arg.span.left_offset.clone();
Tree::default_app(func, token)
@ -968,6 +991,8 @@ pub fn recurse_left_mut_while<'s>(
| Variant::Lambda(_)
| Variant::Array(_)
| Variant::Annotated(_)
| Variant::OperatorFunction(_)
| Variant::OperatorTypeSignature(_)
| Variant::Tuple(_) => break,
// Optional LHS.
Variant::ArgumentBlockApplication(ArgumentBlockApplication { lhs, .. })