mirror of
https://github.com/enso-org/enso.git
synced 2024-12-24 02:42:28 +03:00
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:
parent
e188b15973
commit
0e412044f6
@ -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) {
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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, .. })
|
||||
|
Loading…
Reference in New Issue
Block a user