mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Add unary operators
This commit is contained in:
parent
09c7d75f0d
commit
7ab7fdb88c
16 changed files with 277 additions and 135 deletions
|
@ -12,7 +12,7 @@ For example, parsing would translate this string...
|
||||||
|
|
||||||
...into this `Expr` value:
|
...into this `Expr` value:
|
||||||
|
|
||||||
Operator(Int(1), Plus, Int(2))
|
BinOp(Int(1), Plus, Int(2))
|
||||||
|
|
||||||
> Technically it would be `Box::new(Int(1))` and `Box::new(Int(2))`, but that's beside the point for now.
|
> Technically it would be `Box::new(Int(1))` and `Box::new(Int(2))`, but that's beside the point for now.
|
||||||
|
|
||||||
|
@ -58,10 +58,10 @@ For example, let's say we had this code:
|
||||||
|
|
||||||
The parser will translate this into the following `Expr`:
|
The parser will translate this into the following `Expr`:
|
||||||
|
|
||||||
Operator(
|
BinOp(
|
||||||
Int(1),
|
Int(1),
|
||||||
Plus,
|
Plus,
|
||||||
Operator(Int(2), Minus, Int(3))
|
BinOp(Int(2), Minus, Int(3))
|
||||||
)
|
)
|
||||||
|
|
||||||
The `eval` function will take this `Expr` and translate it into this much simpler `Expr`:
|
The `eval` function will take this `Expr` and translate it into this much simpler `Expr`:
|
||||||
|
@ -74,18 +74,18 @@ At this point it's become so simple that we can display it to the end user as th
|
||||||
|
|
||||||
`eval` accomplishes this by doing a `match` on an `Expr` and resolving every operation it encounters. For example, when it first sees this:
|
`eval` accomplishes this by doing a `match` on an `Expr` and resolving every operation it encounters. For example, when it first sees this:
|
||||||
|
|
||||||
Operator(
|
BinOp(
|
||||||
Int(1),
|
Int(1),
|
||||||
Plus,
|
Plus,
|
||||||
Operator(Int(8), Minus, Int(3))
|
BinOp(Int(8), Minus, Int(3))
|
||||||
)
|
)
|
||||||
|
|
||||||
The first thing it does is to call `eval` on the right `Expr` values on either side of the `Plus`. That results in:
|
The first thing it does is to call `eval` on the right `Expr` values on either side of the `Plus`. That results in:
|
||||||
|
|
||||||
1. Calling `eval` on `Int(1)`, which returns `Int(1)` since it can't be reduced any further.
|
1. Calling `eval` on `Int(1)`, which returns `Int(1)` since it can't be reduced any further.
|
||||||
2. Calling `eval` on `Operator(Int(8), Minus, Int(3))`, which in fact can be reduced further.
|
2. Calling `eval` on `BinOp(Int(8), Minus, Int(3))`, which in fact can be reduced further.
|
||||||
|
|
||||||
Since the second call to `eval` will match on another `Operator`, it's once again going to recursively call `eval` on both of its `Expr` values. Since those are both `Int` values, though, their `eval` calls will return them right away without doing anything else.
|
Since the second call to `eval` will match on another `BinOp`, it's once again going to recursively call `eval` on both of its `Expr` values. Since those are both `Int` values, though, their `eval` calls will return them right away without doing anything else.
|
||||||
|
|
||||||
Now that it's evaluated the expressions on either side of the `Minus`, `eval` will look at the particular operator being applied to those expressoins (in this case, a minus operator) and check to see if the expressions it was given work with that operation.
|
Now that it's evaluated the expressions on either side of the `Minus`, `eval` will look at the particular operator being applied to those expressoins (in this case, a minus operator) and check to see if the expressions it was given work with that operation.
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ Assuming there's no type problem, `eval` can go ahead and run the Rust code of `
|
||||||
|
|
||||||
That concludes our original recursive call to `eval`, after which point we'll be evaluating this expression:
|
That concludes our original recursive call to `eval`, after which point we'll be evaluating this expression:
|
||||||
|
|
||||||
Operator(
|
BinOp(
|
||||||
Int(1),
|
Int(1),
|
||||||
Plus,
|
Plus,
|
||||||
Int(5)
|
Int(5)
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub fn canonicalize_declaration<'a>(
|
||||||
// operator precedence and associativity rules), before doing any other canonicalization.
|
// operator precedence and associativity rules), before doing any other canonicalization.
|
||||||
//
|
//
|
||||||
// If we did this *during* canonicalization, then each time we
|
// If we did this *during* canonicalization, then each time we
|
||||||
// visited an Operator node we'd recursively try to apply this to each of its nested
|
// visited an BinOp node we'd recursively try to apply this to each of its nested
|
||||||
// operators, and thena again on *their* nested operators, ultimately applying the
|
// operators, and thena again on *their* nested operators, ultimately applying the
|
||||||
// rules multiple times unnecessarily.
|
// rules multiple times unnecessarily.
|
||||||
let loc_expr = operator::desugar(arena, &loc_expr);
|
let loc_expr = operator::desugar(arena, &loc_expr);
|
||||||
|
@ -648,8 +648,11 @@ fn canonicalize_expr(
|
||||||
sub_expr
|
sub_expr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ast::Expr::Operator((_, loc_op, _)) => {
|
ast::Expr::BinOp((_, loc_op, _)) => {
|
||||||
panic!("An operator did not get desugared somehow: {:?}", loc_op);
|
panic!("A binary operator did not get desugared somehow: {:?}", loc_op);
|
||||||
|
}
|
||||||
|
ast::Expr::UnaryOp(_, op) => {
|
||||||
|
panic!("A binary operator did not get desugared somehow: {:?}", loc_op);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use operator::Operator::Pizza;
|
use operator::BinOp::Pizza;
|
||||||
use operator::{CalledVia, Operator};
|
use operator::{BinOp, CalledVia};
|
||||||
use parse::ast::Expr::{self, *};
|
use parse::ast::Expr::{self, *};
|
||||||
use parse::ast::{AssignedField, Def};
|
use parse::ast::{AssignedField, Def};
|
||||||
use region::{Located, Region};
|
use region::{Located, Region};
|
||||||
use types;
|
use types;
|
||||||
|
|
||||||
// Operator precedence logic adapted from Gluon by Markus Westerlind, MIT licensed
|
// BinOp precedence logic adapted from Gluon by Markus Westerlind, MIT licensed
|
||||||
// https://github.com/gluon-lang/gluon
|
// https://github.com/gluon-lang/gluon
|
||||||
// Thank you, Markus!
|
// Thank you, Markus!
|
||||||
fn new_op_expr<'a>(
|
fn new_op_expr<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
left: Located<Expr<'a>>,
|
left: Located<Expr<'a>>,
|
||||||
op: Located<Operator>,
|
op: Located<BinOp>,
|
||||||
right: Located<Expr<'a>>,
|
right: Located<Expr<'a>>,
|
||||||
) -> Located<Expr<'a>> {
|
) -> Located<Expr<'a>> {
|
||||||
let new_region = Region {
|
let new_region = Region {
|
||||||
|
@ -23,7 +23,7 @@ fn new_op_expr<'a>(
|
||||||
end_line: right.region.end_line,
|
end_line: right.region.end_line,
|
||||||
end_col: right.region.end_col,
|
end_col: right.region.end_col,
|
||||||
};
|
};
|
||||||
let new_expr = Expr::Operator(arena.alloc((left, op, right)));
|
let new_expr = Expr::BinOp(arena.alloc((left, op, right)));
|
||||||
|
|
||||||
Located {
|
Located {
|
||||||
value: new_expr,
|
value: new_expr,
|
||||||
|
@ -32,7 +32,7 @@ fn new_op_expr<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reorder the expression tree based on operator precedence and associativity rules,
|
/// Reorder the expression tree based on operator precedence and associativity rules,
|
||||||
/// then replace the Operator nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
|
||||||
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
|
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
|
||||||
use operator::Associativity::*;
|
use operator::Associativity::*;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -51,6 +51,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
| MalformedIdent(_)
|
| MalformedIdent(_)
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| PrecedenceConflict(_, _, _)
|
| PrecedenceConflict(_, _, _)
|
||||||
|
| UnaryOp(_, _)
|
||||||
| Variant(_, _) => loc_expr,
|
| Variant(_, _) => loc_expr,
|
||||||
|
|
||||||
Field(sub_expr, paths) => arena.alloc(Located {
|
Field(sub_expr, paths) => arena.alloc(Located {
|
||||||
|
@ -91,10 +92,10 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
value: Closure(loc_patterns, desugar(arena, loc_ret)),
|
value: Closure(loc_patterns, desugar(arena, loc_ret)),
|
||||||
}),
|
}),
|
||||||
Operator(_) => {
|
BinOp(_) => {
|
||||||
let mut infixes = Infixes::new(arena.alloc(loc_expr));
|
let mut infixes = Infixes::new(arena.alloc(loc_expr));
|
||||||
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
|
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
|
||||||
let mut op_stack: Vec<Located<Operator>> = Vec::new_in(arena);
|
let mut op_stack: Vec<Located<BinOp>> = Vec::new_in(arena);
|
||||||
|
|
||||||
while let Some(token) = infixes.next() {
|
while let Some(token) = infixes.next() {
|
||||||
match token {
|
match token {
|
||||||
|
@ -179,7 +180,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
//
|
//
|
||||||
// By design, Roc neither allows custom operators nor has any built-in operators with
|
// By design, Roc neither allows custom operators nor has any built-in operators with
|
||||||
// the same precedence and different associativity, so this should never happen!
|
// the same precedence and different associativity, so this should never happen!
|
||||||
panic!("Operators had the same associativity, but different precedence. This should never happen!");
|
panic!("BinOps had the same associativity, but different precedence. This should never happen!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +216,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_op.region,
|
region: loc_op.region,
|
||||||
});
|
});
|
||||||
|
|
||||||
Apply(loc_expr, args, CalledVia::Operator(binop))
|
Apply(loc_expr, args, CalledVia::BinOp(binop))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -226,7 +227,6 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
|
|
||||||
arg_stack.pop().unwrap()
|
arg_stack.pop().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
Defs(defs, loc_ret) => {
|
Defs(defs, loc_ret) => {
|
||||||
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
|
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
|
||||||
|
|
||||||
|
@ -253,12 +253,8 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Case(
|
Case(loc_cond_expr, branches) => {
|
||||||
loc_cond_expr,
|
|
||||||
branches, // Vec<'a, &'a (Loc<Pattern<'a>>, Loc<Expr<'a>>)>,
|
|
||||||
) => {
|
|
||||||
let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr));
|
let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr));
|
||||||
|
|
||||||
let desugared_branches = Vec::with_capacity_in(branches.len(), arena);
|
let desugared_branches = Vec::with_capacity_in(branches.len(), arena);
|
||||||
|
|
||||||
arena.alloc(Located {
|
arena.alloc(Located {
|
||||||
|
@ -266,8 +262,31 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
UnaryOp(loc_arg, loc_op) => {
|
||||||
|
use operator::UnaryOp::*;
|
||||||
|
|
||||||
|
let region = loc_op.region;
|
||||||
|
let op = loc_op.value;
|
||||||
|
let value = match op {
|
||||||
|
Negate => Var(
|
||||||
|
bumpalo::vec![in arena; types::MOD_NUM].into_bump_slice(),
|
||||||
|
"negate",
|
||||||
|
),
|
||||||
|
Not => Var(
|
||||||
|
bumpalo::vec![in arena; types::MOD_BOOL].into_bump_slice(),
|
||||||
|
"not",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let loc_fn_var = arena.alloc(Located { region, value });
|
||||||
|
let desugared_args = bumpalo::vec![in arena; desugar(arena, loc_arg)];
|
||||||
|
|
||||||
|
arena.alloc(Located {
|
||||||
|
value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)),
|
||||||
|
region: loc_expr.region,
|
||||||
|
})
|
||||||
|
}
|
||||||
SpaceBefore(expr, _) => {
|
SpaceBefore(expr, _) => {
|
||||||
// Since we've already begun canonicalization, these are no longer needed
|
// Since we've already begun canonicalization, spaces are no longer needed
|
||||||
// and should be dropped.
|
// and should be dropped.
|
||||||
desugar(
|
desugar(
|
||||||
arena,
|
arena,
|
||||||
|
@ -285,7 +304,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SpaceAfter(expr, _) => {
|
SpaceAfter(expr, _) => {
|
||||||
// Since we've already begun canonicalization, these are no longer needed
|
// Since we've already begun canonicalization, spaces are no longer needed
|
||||||
// and should be dropped.
|
// and should be dropped.
|
||||||
desugar(
|
desugar(
|
||||||
arena,
|
arena,
|
||||||
|
@ -346,8 +365,8 @@ fn desugar_field<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn desugar_binop<'a>(binop: &Operator, arena: &'a Bump) -> (&'a [&'a str], &'a str) {
|
fn desugar_binop<'a>(binop: &BinOp, arena: &'a Bump) -> (&'a [&'a str], &'a str) {
|
||||||
use self::Operator::*;
|
use self::BinOp::*;
|
||||||
|
|
||||||
match binop {
|
match binop {
|
||||||
Caret => (
|
Caret => (
|
||||||
|
@ -421,7 +440,7 @@ fn desugar_binop<'a>(binop: &Operator, arena: &'a Bump) -> (&'a [&'a str], &'a s
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum InfixToken<'a> {
|
enum InfixToken<'a> {
|
||||||
Arg(&'a Located<Expr<'a>>),
|
Arg(&'a Located<Expr<'a>>),
|
||||||
Op(Located<Operator>),
|
Op(Located<BinOp>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator that takes an expression that has had its operators grouped
|
/// An iterator that takes an expression that has had its operators grouped
|
||||||
|
@ -452,7 +471,7 @@ struct Infixes<'a> {
|
||||||
/// The next part of the expression that we need to flatten
|
/// The next part of the expression that we need to flatten
|
||||||
remaining_expr: Option<&'a Located<Expr<'a>>>,
|
remaining_expr: Option<&'a Located<Expr<'a>>>,
|
||||||
/// Cached operator from a previous iteration
|
/// Cached operator from a previous iteration
|
||||||
next_op: Option<Located<Operator>>,
|
next_op: Option<Located<BinOp>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Infixes<'a> {
|
impl<'a> Infixes<'a> {
|
||||||
|
@ -474,7 +493,7 @@ impl<'a> Iterator for Infixes<'a> {
|
||||||
.remaining_expr
|
.remaining_expr
|
||||||
.take()
|
.take()
|
||||||
.map(|loc_expr| match loc_expr.value {
|
.map(|loc_expr| match loc_expr.value {
|
||||||
Expr::Operator((left, op, right)) => {
|
Expr::BinOp((left, op, right)) => {
|
||||||
self.remaining_expr = Some(right);
|
self.remaining_expr = Some(right);
|
||||||
self.next_op = Some(op.clone());
|
self.next_op = Some(op.clone());
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use can::pattern::PatternType;
|
use can::pattern::PatternType;
|
||||||
use ident::{Ident, VariantName};
|
use ident::{Ident, VariantName};
|
||||||
use operator::Operator;
|
use operator::BinOp;
|
||||||
use region::{Located, Region};
|
use region::{Located, Region};
|
||||||
|
|
||||||
/// Problems that can occur in the course of canonicalization.
|
/// Problems that can occur in the course of canonicalization.
|
||||||
|
@ -21,7 +21,7 @@ pub enum Problem {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum PrecedenceProblem {
|
pub enum PrecedenceProblem {
|
||||||
BothNonAssociative(Located<Operator>, Located<Operator>),
|
BothNonAssociative(Located<BinOp>, Located<BinOp>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use operator::Operator;
|
use operator::BinOp;
|
||||||
use region::Located;
|
use region::Located;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub enum Expr {
|
||||||
|
|
||||||
// Sugar
|
// Sugar
|
||||||
If(Box<Located<Expr>>, Box<Located<Expr>>, Box<Located<Expr>>),
|
If(Box<Located<Expr>>, Box<Located<Expr>>, Box<Located<Expr>>),
|
||||||
Operator(Box<Located<Expr>>, Located<Operator>, Box<Located<Expr>>),
|
BinOp(Box<Located<Expr>>, Located<BinOp>, Box<Located<Expr>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -112,7 +112,7 @@ impl Expr {
|
||||||
.map(|(pattern, body)| (pattern, body.with_value(body.value.walk(transform))))
|
.map(|(pattern, body)| (pattern, body.with_value(body.value.walk(transform))))
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
Operator(loc_left, loc_op, loc_right) => Operator(
|
BinOp(loc_left, loc_op, loc_right) => BinOp(
|
||||||
Box::new(loc_left.with_value(loc_left.value.walk(transform))),
|
Box::new(loc_left.with_value(loc_left.value.walk(transform))),
|
||||||
loc_op,
|
loc_op,
|
||||||
Box::new(loc_right.with_value(loc_right.value.walk(transform))),
|
Box::new(loc_right.with_value(loc_right.value.walk(transform))),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use self::Operator::*;
|
use self::BinOp::*;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
@ -7,14 +7,22 @@ pub enum CalledVia {
|
||||||
Space,
|
Space,
|
||||||
|
|
||||||
/// Calling with an operator, e.g. (bar |> foo) or (1 + 2)
|
/// Calling with an operator, e.g. (bar |> foo) or (1 + 2)
|
||||||
Operator(Operator),
|
BinOp(BinOp),
|
||||||
|
|
||||||
/// Calling with the unary (!) operator, e.g. (!foo bar baz)
|
/// Calling with a unary operator, e.g. (!foo bar baz) or (-foo bar baz)
|
||||||
UnaryNot,
|
UnaryOp(UnaryOp),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Operator {
|
pub enum UnaryOp {
|
||||||
|
/// (-), e.g. (-x)
|
||||||
|
Negate,
|
||||||
|
/// (!), e.g. (!x)
|
||||||
|
Not,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum BinOp {
|
||||||
// highest precedence
|
// highest precedence
|
||||||
Caret,
|
Caret,
|
||||||
Star,
|
Star,
|
||||||
|
@ -62,7 +70,7 @@ pub enum Associativity {
|
||||||
NonAssociative,
|
NonAssociative,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operator {
|
impl BinOp {
|
||||||
pub fn associativity(&self) -> Associativity {
|
pub fn associativity(&self) -> Associativity {
|
||||||
use self::Associativity::*;
|
use self::Associativity::*;
|
||||||
|
|
||||||
|
@ -90,13 +98,13 @@ impl Operator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Operator {
|
impl PartialOrd for BinOp {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Operator {
|
impl Ord for BinOp {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.precedence().cmp(&other.precedence())
|
self.precedence().cmp(&other.precedence())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use bumpalo::collections::String;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use operator::CalledVia;
|
use operator::CalledVia;
|
||||||
use operator::Operator;
|
use operator::{BinOp, UnaryOp};
|
||||||
use parse::ident::Ident;
|
use parse::ident::Ident;
|
||||||
use region::{Loc, Region};
|
use region::{Loc, Region};
|
||||||
|
|
||||||
|
@ -137,7 +137,8 @@ pub enum Expr<'a> {
|
||||||
/// To apply by name, do Apply(Var(...), ...)
|
/// To apply by name, do Apply(Var(...), ...)
|
||||||
/// To apply a variant by name, do Apply(Variant(...), ...)
|
/// To apply a variant by name, do Apply(Variant(...), ...)
|
||||||
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
Apply(&'a Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>, CalledVia),
|
||||||
Operator(&'a (Loc<Expr<'a>>, Loc<Operator>, Loc<Expr<'a>>)),
|
BinOp(&'a (Loc<Expr<'a>>, Loc<BinOp>, Loc<Expr<'a>>)),
|
||||||
|
UnaryOp(&'a Loc<Expr<'a>>, Loc<UnaryOp>),
|
||||||
|
|
||||||
// Conditionals
|
// Conditionals
|
||||||
If(&'a (Loc<Expr<'a>>, Loc<Expr<'a>>, Loc<Expr<'a>>)),
|
If(&'a (Loc<Expr<'a>>, Loc<Expr<'a>>, Loc<Expr<'a>>)),
|
||||||
|
@ -156,7 +157,7 @@ pub enum Expr<'a> {
|
||||||
MalformedClosure,
|
MalformedClosure,
|
||||||
// Both operators were non-associative, e.g. (True == False == False).
|
// Both operators were non-associative, e.g. (True == False == False).
|
||||||
// We should tell the author to disambiguate by grouping them with parens.
|
// We should tell the author to disambiguate by grouping them with parens.
|
||||||
PrecedenceConflict(Loc<Operator>, Loc<Operator>, &'a Loc<Expr<'a>>),
|
PrecedenceConflict(Loc<BinOp>, Loc<BinOp>, &'a Loc<Expr<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
184
src/parse/mod.rs
184
src/parse/mod.rs
|
@ -25,7 +25,7 @@ pub mod type_annotation;
|
||||||
/// parsing the file
|
/// parsing the file
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use operator::{CalledVia, Operator};
|
use operator::{BinOp, CalledVia, UnaryOp};
|
||||||
use parse;
|
use parse;
|
||||||
use parse::ast::{
|
use parse::ast::{
|
||||||
AppHeader, AssignedField, Attempting, CommentOrNewline, Def, Expr, HeaderEntry,
|
AppHeader, AssignedField, Attempting, CommentOrNewline, Def, Expr, HeaderEntry,
|
||||||
|
@ -39,12 +39,12 @@ use parse::ident::{ident, unqualified_ident, variant_or_ident, Ident};
|
||||||
use parse::number_literal::number_literal;
|
use parse::number_literal::number_literal;
|
||||||
use parse::parser::{
|
use parse::parser::{
|
||||||
allocated, and, attempt, between, char, either, loc, map, map_with_arena, not, not_followed_by,
|
allocated, and, attempt, between, char, either, loc, map, map_with_arena, not, not_followed_by,
|
||||||
one_of16, one_of2, one_of5, one_of6, one_of9, one_or_more, optional, skip_first, skip_second,
|
one_of10, one_of16, one_of2, one_of5, one_of6, one_or_more, optional, skip_first, skip_second,
|
||||||
string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult,
|
string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult,
|
||||||
Parser, State,
|
Parser, State,
|
||||||
};
|
};
|
||||||
use parse::record::record;
|
use parse::record::record;
|
||||||
use region::Located;
|
use region::{Located, Region};
|
||||||
|
|
||||||
pub fn module<'a>() -> impl Parser<'a, Module<'a>> {
|
pub fn module<'a>() -> impl Parser<'a, Module<'a>> {
|
||||||
one_of2(interface_module(), app_module())
|
one_of2(interface_module(), app_module())
|
||||||
|
@ -147,17 +147,6 @@ fn mod_header_entry<'a>() -> impl Parser<'a, HeaderEntry<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn app<'a>() -> impl Parser<'a, Module<'a>> {
|
|
||||||
// skip_first(string("app using Echo"))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn api_bridge<'a>() -> impl Parser<'a, Module<'a>> {
|
|
||||||
// and(
|
|
||||||
// skip_first(string("api bridge"), space1_around(ident())),
|
|
||||||
// skip_first(string("exposes"), space1_around(ident())),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
// Recursive parsers must not directly invoke functions which return (impl Parser),
|
// Recursive parsers must not directly invoke functions which return (impl Parser),
|
||||||
// as this causes rustc to stack overflow. Thus, parse_expr must be a
|
// as this causes rustc to stack overflow. Thus, parse_expr must be a
|
||||||
|
@ -170,13 +159,14 @@ fn loc_parse_expr_body_without_operators<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>> {
|
) -> ParseResult<'a, Located<Expr<'a>>> {
|
||||||
one_of9(
|
one_of10(
|
||||||
loc_parenthetical_expr(min_indent),
|
loc_parenthetical_expr(min_indent),
|
||||||
loc(string_literal()),
|
loc(string_literal()),
|
||||||
loc(number_literal()),
|
loc(number_literal()),
|
||||||
loc(closure(min_indent)),
|
loc(closure(min_indent)),
|
||||||
loc(record_literal(min_indent)),
|
loc(record_literal(min_indent)),
|
||||||
loc(list_literal(min_indent)),
|
loc(list_literal(min_indent)),
|
||||||
|
loc(unary_op(min_indent)),
|
||||||
loc(case_expr(min_indent)),
|
loc(case_expr(min_indent)),
|
||||||
loc(if_expr(min_indent)),
|
loc(if_expr(min_indent)),
|
||||||
loc(ident_etc(min_indent)),
|
loc(ident_etc(min_indent)),
|
||||||
|
@ -184,6 +174,28 @@ fn loc_parse_expr_body_without_operators<'a>(
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unary (!) or (-)
|
||||||
|
///
|
||||||
|
/// e.g. `!x` or `-x`
|
||||||
|
pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
|
one_of2(
|
||||||
|
map_with_arena(
|
||||||
|
skip_first(
|
||||||
|
char('!'),
|
||||||
|
loc(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||||
|
),
|
||||||
|
|arena, loc_expr| Expr::UnaryOp(arena.alloc(loc_expr), UnaryOp::Not),
|
||||||
|
),
|
||||||
|
map_with_arena(
|
||||||
|
skip_first(
|
||||||
|
char('-'),
|
||||||
|
loc(move |arena, state| parse_expr(min_indent, arena, state)),
|
||||||
|
),
|
||||||
|
|arena, loc_expr| Expr::UnaryOp(arena.alloc(loc_expr), UnaryOp::Negate),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Expr<'a>> {
|
fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Expr<'a>> {
|
||||||
let expr_parser = map_with_arena(
|
let expr_parser = map_with_arena(
|
||||||
and(
|
and(
|
||||||
|
@ -191,11 +203,11 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
|
||||||
move |arena, state| loc_parse_expr_body_without_operators(min_indent, arena, state),
|
move |arena, state| loc_parse_expr_body_without_operators(min_indent, arena, state),
|
||||||
// Parse the operator, with optional spaces before it.
|
// Parse the operator, with optional spaces before it.
|
||||||
//
|
//
|
||||||
// Since spaces can only wrap an Expr, not an Operator, we have to first
|
// Since spaces can only wrap an Expr, not an BinOp, we have to first
|
||||||
// parse the spaces and then attach them retroactively to the expression
|
// parse the spaces and then attach them retroactively to the expression
|
||||||
// preceding the operator (the one we parsed before considering operators).
|
// preceding the operator (the one we parsed before considering operators).
|
||||||
optional(and(
|
optional(and(
|
||||||
and(space0(min_indent), loc(operator())),
|
and(space0(min_indent), loc(binop())),
|
||||||
// The spaces *after* the operator can be attached directly to
|
// The spaces *after* the operator can be attached directly to
|
||||||
// the expression following the operator.
|
// the expression following the operator.
|
||||||
space0_before(
|
space0_before(
|
||||||
|
@ -216,7 +228,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
|
||||||
};
|
};
|
||||||
let tuple = arena.alloc((loc_expr1, loc_op, loc_expr2));
|
let tuple = arena.alloc((loc_expr1, loc_op, loc_expr2));
|
||||||
|
|
||||||
Expr::Operator(tuple)
|
Expr::BinOp(tuple)
|
||||||
}
|
}
|
||||||
None => loc_expr1.value,
|
None => loc_expr1.value,
|
||||||
},
|
},
|
||||||
|
@ -403,7 +415,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
||||||
| Expr::Field(_, _)
|
| Expr::Field(_, _)
|
||||||
| Expr::List(_)
|
| Expr::List(_)
|
||||||
| Expr::Closure(_, _)
|
| Expr::Closure(_, _)
|
||||||
| Expr::Operator(_)
|
| Expr::BinOp(_)
|
||||||
| Expr::Defs(_, _)
|
| Expr::Defs(_, _)
|
||||||
| Expr::If(_)
|
| Expr::If(_)
|
||||||
| Expr::Case(_, _)
|
| Expr::Case(_, _)
|
||||||
|
@ -635,13 +647,14 @@ fn loc_parse_function_arg<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>> {
|
) -> ParseResult<'a, Located<Expr<'a>>> {
|
||||||
one_of9(
|
one_of10(
|
||||||
loc_parenthetical_expr(min_indent),
|
loc_parenthetical_expr(min_indent),
|
||||||
loc(string_literal()),
|
loc(string_literal()),
|
||||||
loc(number_literal()),
|
loc(number_literal()),
|
||||||
loc(closure(min_indent)),
|
loc(closure(min_indent)),
|
||||||
loc(record_literal(min_indent)),
|
loc(record_literal(min_indent)),
|
||||||
loc(list_literal(min_indent)),
|
loc(list_literal(min_indent)),
|
||||||
|
loc(unary_op(min_indent)),
|
||||||
loc(case_expr(min_indent)),
|
loc(case_expr(min_indent)),
|
||||||
loc(if_expr(min_indent)),
|
loc(if_expr(min_indent)),
|
||||||
loc(ident_without_apply()),
|
loc(ident_without_apply()),
|
||||||
|
@ -915,7 +928,102 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> {
|
pub fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> {
|
||||||
one_or_more(space1_before(loc_function_arg(min_indent), min_indent))
|
// The rules for (-) are special-cased, and they come up here. They work like this:
|
||||||
|
//
|
||||||
|
// x - y # "x minus y"
|
||||||
|
// x-y # "x minus y"
|
||||||
|
// x- y # "x minus y" (probably written in a rush)
|
||||||
|
// x -y # "call x, passing (-y)"
|
||||||
|
//
|
||||||
|
// Since operators have higher precedence than function application,
|
||||||
|
// any time we encounter a '-' we need to check the spaces around it
|
||||||
|
// to see if it's a binop. If it is, we're immediately done parsing args!
|
||||||
|
one_or_more(move |arena, original_state| {
|
||||||
|
{
|
||||||
|
// First, parse spaces. (We have to do this regardless, because these are
|
||||||
|
// function args we're parsing.)
|
||||||
|
//
|
||||||
|
// The guaranteed presence of spaces means we don't need to consider
|
||||||
|
// the (x-y) and (x- y) scenarios, since those can only come up when
|
||||||
|
// the '-' has no spaces before it.
|
||||||
|
then(space1(min_indent), |arena, state, spaces| {
|
||||||
|
let mut chars = state.input.chars();
|
||||||
|
|
||||||
|
// First, make sure there's actually a '-' character next.
|
||||||
|
// Otherwise, it's definitely neither a unary nor a binary (-) operator!
|
||||||
|
let (loc_expr, state) = if let Some('-') = chars.next() {
|
||||||
|
// Now look ahead to see if there's a space after it.
|
||||||
|
// We won't consume that space, so we don't need to parse the
|
||||||
|
// entire thing - just the first char of it.
|
||||||
|
if let Some(potential_space) = chars.next() {
|
||||||
|
match potential_space {
|
||||||
|
' ' | '\n' | '#' => {
|
||||||
|
// This is '-' with space(s) both before it and after it,
|
||||||
|
// e.g. (x - y)
|
||||||
|
//
|
||||||
|
// This must be a binary (-) operator, not unary!
|
||||||
|
// That means we're immediately done parsing function arguments.
|
||||||
|
//
|
||||||
|
// Error out so the binop parsing logic can take over from here.
|
||||||
|
return Err((
|
||||||
|
Fail {
|
||||||
|
reason: FailReason::Unexpected('-', state.len_region(1)),
|
||||||
|
attempting: state.attempting,
|
||||||
|
},
|
||||||
|
// Backtrack to before we parsed the spaces;
|
||||||
|
// the binop is going to parse those again.
|
||||||
|
original_state,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// This is '-' with space(s) before it, but none after,
|
||||||
|
// e.g. (x -y)
|
||||||
|
//
|
||||||
|
// This must be a unary (-) operator, not binary!
|
||||||
|
//
|
||||||
|
// All we consumed here was the one '-' char, so
|
||||||
|
// calculate the region and new state based on that.
|
||||||
|
let region = Region {
|
||||||
|
start_col: state.column,
|
||||||
|
start_line: state.line,
|
||||||
|
end_col: state.column + 1,
|
||||||
|
end_line: state.line,
|
||||||
|
};
|
||||||
|
let state = state.advance_without_indenting(1)?;
|
||||||
|
// Continue parsing the function arg as normal.
|
||||||
|
let (loc_expr, state) =
|
||||||
|
loc_function_arg(min_indent).parse(arena, state)?;
|
||||||
|
let value = Expr::UnaryOp(arena.alloc(loc_expr), UnaryOp::Negate);
|
||||||
|
|
||||||
|
(Located { region, value }, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// EOF immediately after (-) is a syntax error.
|
||||||
|
return Err((
|
||||||
|
Fail {
|
||||||
|
reason: FailReason::Eof(state.len_region(1)),
|
||||||
|
attempting: state.attempting,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// There isn't a '-' next, so proceed as normal.
|
||||||
|
loc_function_arg(min_indent).parse(arena, state)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Located {
|
||||||
|
region: loc_expr.region,
|
||||||
|
value: Expr::SpaceBefore(arena.alloc(loc_expr.value), spaces),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.parse(arena, original_state)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When we parse an ident like `foo ` it could be any of these:
|
/// When we parse an ident like `foo ` it could be any of these:
|
||||||
|
@ -1043,26 +1151,26 @@ fn ident_to_expr<'a>(src: Ident<'a>) -> Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operator<'a>() -> impl Parser<'a, Operator> {
|
fn binop<'a>() -> impl Parser<'a, BinOp> {
|
||||||
one_of16(
|
one_of16(
|
||||||
// Sorted from highest to lowest predicted usage in practice,
|
// Sorted from highest to lowest predicted usage in practice,
|
||||||
// so that successful matches shorrt-circuit as early as possible.
|
// so that successful matches shorrt-circuit as early as possible.
|
||||||
map(string("|>"), |_| Operator::Pizza),
|
map(string("|>"), |_| BinOp::Pizza),
|
||||||
map(string("=="), |_| Operator::Equals),
|
map(string("=="), |_| BinOp::Equals),
|
||||||
map(string("&&"), |_| Operator::And),
|
map(string("&&"), |_| BinOp::And),
|
||||||
map(string("||"), |_| Operator::Or),
|
map(string("||"), |_| BinOp::Or),
|
||||||
map(char('+'), |_| Operator::Plus),
|
map(char('+'), |_| BinOp::Plus),
|
||||||
map(char('-'), |_| Operator::Minus),
|
map(char('*'), |_| BinOp::Star),
|
||||||
map(char('*'), |_| Operator::Star),
|
map(char('-'), |_| BinOp::Minus),
|
||||||
map(char('/'), |_| Operator::Slash),
|
map(char('/'), |_| BinOp::Slash),
|
||||||
map(char('<'), |_| Operator::LessThan),
|
map(char('<'), |_| BinOp::LessThan),
|
||||||
map(char('>'), |_| Operator::GreaterThan),
|
map(char('>'), |_| BinOp::GreaterThan),
|
||||||
map(string("<="), |_| Operator::LessThanOrEq),
|
map(string("<="), |_| BinOp::LessThanOrEq),
|
||||||
map(string(">="), |_| Operator::GreaterThanOrEq),
|
map(string(">="), |_| BinOp::GreaterThanOrEq),
|
||||||
map(char('^'), |_| Operator::Caret),
|
map(char('^'), |_| BinOp::Caret),
|
||||||
map(char('%'), |_| Operator::Percent),
|
map(char('%'), |_| BinOp::Percent),
|
||||||
map(string("//"), |_| Operator::DoubleSlash),
|
map(string("//"), |_| BinOp::DoubleSlash),
|
||||||
map(string("%%"), |_| Operator::DoublePercent),
|
map(string("%%"), |_| BinOp::DoublePercent),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,22 @@ impl<'a> State<'a> {
|
||||||
_ => Err(line_too_long(self.attempting, self.clone())),
|
_ => Err(line_too_long(self.attempting, self.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a Region corresponding to the current state, but
|
||||||
|
/// with the end_col advanced by the given amount. This is
|
||||||
|
/// useful when parsing something "manually" (using input.chars())
|
||||||
|
/// and thus wanting a Region while not having access to loc().
|
||||||
|
pub fn len_region(&self, length: u16) -> Region {
|
||||||
|
Region {
|
||||||
|
start_col: self.column,
|
||||||
|
start_line: self.line,
|
||||||
|
end_col: self
|
||||||
|
.column
|
||||||
|
.checked_add(length)
|
||||||
|
.unwrap_or_else(|| panic!("len_region overflowed")),
|
||||||
|
end_line: self.line,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_p
|
||||||
),
|
),
|
||||||
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
EmptyRecord => buf.push_str(EMPTY_RECORD),
|
||||||
Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
|
Func(args, ret) => write_fn(args, ret, subs, buf, use_parens),
|
||||||
Operator(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
|
BinOp(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens),
|
||||||
Erroneous(problem) => {
|
Erroneous(problem) => {
|
||||||
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,12 +108,12 @@ fn type_to_variable<'a>(subs: &'a mut Subs, typ: Type) -> Variable {
|
||||||
|
|
||||||
subs.fresh(Descriptor::from(content))
|
subs.fresh(Descriptor::from(content))
|
||||||
}
|
}
|
||||||
Operator(box_type) => {
|
BinOp(box_type) => {
|
||||||
let op_type = *box_type;
|
let op_type = *box_type;
|
||||||
let l_var = type_to_variable(subs, op_type.left);
|
let l_var = type_to_variable(subs, op_type.left);
|
||||||
let r_var = type_to_variable(subs, op_type.right);
|
let r_var = type_to_variable(subs, op_type.right);
|
||||||
let ret_var = type_to_variable(subs, op_type.ret);
|
let ret_var = type_to_variable(subs, op_type.ret);
|
||||||
let content = Content::Structure(FlatType::Operator(l_var, r_var, ret_var));
|
let content = Content::Structure(FlatType::BinOp(l_var, r_var, ret_var));
|
||||||
|
|
||||||
subs.fresh(Descriptor::from(content))
|
subs.fresh(Descriptor::from(content))
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ pub enum FlatType {
|
||||||
args: Vec<Variable>,
|
args: Vec<Variable>,
|
||||||
},
|
},
|
||||||
Func(Vec<Variable>, Variable),
|
Func(Vec<Variable>, Variable),
|
||||||
Operator(Variable, Variable, Variable),
|
BinOp(Variable, Variable, Variable),
|
||||||
Erroneous(Problem),
|
Erroneous(Problem),
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
}
|
}
|
||||||
|
|
29
src/types.rs
29
src/types.rs
|
@ -1,13 +1,13 @@
|
||||||
use can::symbol::Symbol;
|
use can::symbol::Symbol;
|
||||||
use collections::ImMap;
|
use collections::ImMap;
|
||||||
use operator::{ArgSide, Operator};
|
use operator::{ArgSide, BinOp};
|
||||||
use region::Located;
|
use region::Located;
|
||||||
use region::Region;
|
use region::Region;
|
||||||
use subs::Variable;
|
use subs::Variable;
|
||||||
|
|
||||||
// The standard modules
|
// The standard modules
|
||||||
pub const MOD_FLOAT: &'static str = "Float";
|
pub const MOD_FLOAT: &'static str = "Float";
|
||||||
pub const MOD_BOOL: &'static str = "Float";
|
pub const MOD_BOOL: &'static str = "Bool";
|
||||||
pub const MOD_INT: &'static str = "Int";
|
pub const MOD_INT: &'static str = "Int";
|
||||||
pub const MOD_STR: &'static str = "Str";
|
pub const MOD_STR: &'static str = "Str";
|
||||||
pub const MOD_LIST: &'static str = "List";
|
pub const MOD_LIST: &'static str = "List";
|
||||||
|
@ -25,7 +25,7 @@ pub enum Type {
|
||||||
EmptyRec,
|
EmptyRec,
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
Function(Vec<Type>, Box<Type>),
|
Function(Vec<Type>, Box<Type>),
|
||||||
Operator(Box<OperatorType>),
|
BinOp(Box<BinOpType>),
|
||||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||||
Apply {
|
Apply {
|
||||||
module_name: Box<str>,
|
module_name: Box<str>,
|
||||||
|
@ -38,19 +38,6 @@ pub enum Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
pub fn for_operator(op: Operator) -> OperatorType {
|
|
||||||
use self::Operator::*;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
Slash => op_type(Type::float(), Type::float(), Type::float()),
|
|
||||||
DoubleSlash => op_type(Type::int(), Type::int(), Type::int()),
|
|
||||||
// TODO actually, don't put these in types.rs - instead, replace them
|
|
||||||
// with an equivalence to their corresponding stdlib functions - e.g.
|
|
||||||
// Slash generates a new variable and an Eq constraint with Float.div.
|
|
||||||
_ => panic!("TODO types for operator {:?}", op),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn num(args: Vec<Type>) -> Self {
|
pub fn num(args: Vec<Type>) -> Self {
|
||||||
Type::Apply {
|
Type::Apply {
|
||||||
module_name: MOD_NUM.into(),
|
module_name: MOD_NUM.into(),
|
||||||
|
@ -97,12 +84,12 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_type(left: Type, right: Type, ret: Type) -> OperatorType {
|
fn op_type(left: Type, right: Type, ret: Type) -> BinOpType {
|
||||||
OperatorType { left, right, ret }
|
BinOpType { left, right, ret }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct OperatorType {
|
pub struct BinOpType {
|
||||||
pub left: Type,
|
pub left: Type,
|
||||||
pub right: Type,
|
pub right: Type,
|
||||||
pub ret: Type,
|
pub ret: Type,
|
||||||
|
@ -137,8 +124,8 @@ pub enum Reason {
|
||||||
NamedFnArg(String /* function name */, u8 /* arg index */),
|
NamedFnArg(String /* function name */, u8 /* arg index */),
|
||||||
AnonymousFnCall(u8 /* arity */),
|
AnonymousFnCall(u8 /* arity */),
|
||||||
NamedFnCall(String /* function name */, u8 /* arity */),
|
NamedFnCall(String /* function name */, u8 /* arity */),
|
||||||
OperatorArg(Operator, ArgSide),
|
BinOpArg(BinOp, ArgSide),
|
||||||
OperatorRet(Operator),
|
BinOpRet(BinOp),
|
||||||
FloatLiteral,
|
FloatLiteral,
|
||||||
IntLiteral,
|
IntLiteral,
|
||||||
InterpolatedStringVar,
|
InterpolatedStringVar,
|
||||||
|
|
|
@ -94,11 +94,11 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri
|
||||||
from_content(Error(Problem::MissingArguments))
|
from_content(Error(Problem::MissingArguments))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Operator(l_l_arg, l_r_arg, l_ret), Operator(r_l_arg, r_r_arg, r_ret)) => {
|
(BinOp(l_l_arg, l_r_arg, l_ret), BinOp(r_l_arg, r_r_arg, r_ret)) => {
|
||||||
let l_arg = union_vars(subs, l_l_arg.clone(), r_l_arg.clone());
|
let l_arg = union_vars(subs, l_l_arg.clone(), r_l_arg.clone());
|
||||||
let r_arg = union_vars(subs, l_r_arg.clone(), r_r_arg.clone());
|
let r_arg = union_vars(subs, l_r_arg.clone(), r_r_arg.clone());
|
||||||
let ret = union_vars(subs, l_ret.clone(), r_ret.clone());
|
let ret = union_vars(subs, l_ret.clone(), r_ret.clone());
|
||||||
let flat_type = Operator(l_arg, r_arg, ret);
|
let flat_type = BinOp(l_arg, r_arg, ret);
|
||||||
|
|
||||||
from_content(Structure(flat_type))
|
from_content(Structure(flat_type))
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,9 +157,9 @@ mod test_canonicalize {
|
||||||
// is_self_tail_recursive: false,
|
// is_self_tail_recursive: false,
|
||||||
// definition: Region::zero(),
|
// definition: Region::zero(),
|
||||||
// args: vec![loc(Pattern::Identifier(sym("arg")))],
|
// args: vec![loc(Pattern::Identifier(sym("arg")))],
|
||||||
// body: loc(Expr::Operator(
|
// body: loc(Expr::BinOp(
|
||||||
// loc_box(Expr::Var(sym("arg"))),
|
// loc_box(Expr::Var(sym("arg"))),
|
||||||
// loc(Operator::Plus),
|
// loc(BinOp::Plus),
|
||||||
// loc_box(Expr::Int(1))
|
// loc_box(Expr::Int(1))
|
||||||
// )),
|
// )),
|
||||||
// references: References {
|
// references: References {
|
||||||
|
@ -631,11 +631,11 @@ mod test_canonicalize {
|
||||||
//// fn two_operator_precedence() {
|
//// fn two_operator_precedence() {
|
||||||
//// assert_eq!(
|
//// assert_eq!(
|
||||||
//// parse_with_precedence("x + y * 5"),
|
//// parse_with_precedence("x + y * 5"),
|
||||||
//// Ok((Operator(
|
//// Ok((BinOp(
|
||||||
//// loc_box(var("x")),
|
//// loc_box(var("x")),
|
||||||
//// loc(Plus),
|
//// loc(Plus),
|
||||||
//// loc_box(
|
//// loc_box(
|
||||||
//// Operator(
|
//// BinOp(
|
||||||
//// loc_box(var("y")),
|
//// loc_box(var("y")),
|
||||||
//// loc(Star),
|
//// loc(Star),
|
||||||
//// loc_box(Int(5))
|
//// loc_box(Int(5))
|
||||||
|
@ -647,9 +647,9 @@ mod test_canonicalize {
|
||||||
|
|
||||||
//// assert_eq!(
|
//// assert_eq!(
|
||||||
//// parse_with_precedence("x * y + 5"),
|
//// parse_with_precedence("x * y + 5"),
|
||||||
//// Ok((Operator(
|
//// Ok((BinOp(
|
||||||
//// loc_box(
|
//// loc_box(
|
||||||
//// Operator(
|
//// BinOp(
|
||||||
//// loc_box(var("x")),
|
//// loc_box(var("x")),
|
||||||
//// loc(Star),
|
//// loc(Star),
|
||||||
//// loc_box(var("y")),
|
//// loc_box(var("y")),
|
||||||
|
@ -666,9 +666,9 @@ mod test_canonicalize {
|
||||||
//// fn compare_and() {
|
//// fn compare_and() {
|
||||||
//// assert_eq!(
|
//// assert_eq!(
|
||||||
//// parse_with_precedence("x > 1 || True"),
|
//// parse_with_precedence("x > 1 || True"),
|
||||||
//// Ok((Operator(
|
//// Ok((BinOp(
|
||||||
//// loc_box(
|
//// loc_box(
|
||||||
//// Operator(
|
//// BinOp(
|
||||||
//// loc_box(var("x")),
|
//// loc_box(var("x")),
|
||||||
//// loc(GreaterThan),
|
//// loc(GreaterThan),
|
||||||
//// loc_box(Int(1))
|
//// loc_box(Int(1))
|
||||||
|
|
|
@ -18,7 +18,7 @@ mod test_parse {
|
||||||
use bumpalo::{self, Bump};
|
use bumpalo::{self, Bump};
|
||||||
use helpers::parse_with;
|
use helpers::parse_with;
|
||||||
use roc::operator::CalledVia;
|
use roc::operator::CalledVia;
|
||||||
use roc::operator::Operator::*;
|
use roc::operator::BinOp::*;
|
||||||
use roc::parse::ast::CommentOrNewline::*;
|
use roc::parse::ast::CommentOrNewline::*;
|
||||||
use roc::parse::ast::Expr::{self, *};
|
use roc::parse::ast::Expr::{self, *};
|
||||||
use roc::parse::ast::Pattern::{self, *};
|
use roc::parse::ast::Pattern::{self, *};
|
||||||
|
@ -256,7 +256,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 1, 2, Plus),
|
Located::new(0, 0, 1, 2, Plus),
|
||||||
Located::new(0, 0, 2, 3, Int("2")),
|
Located::new(0, 0, 2, 3, Int("2")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1+2");
|
let actual = parse_with(&arena, "1+2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -270,7 +270,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 4, Plus),
|
Located::new(0, 0, 3, 4, Plus),
|
||||||
Located::new(0, 0, 7, 8, Int("2")),
|
Located::new(0, 0, 7, 8, Int("2")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1 + 2");
|
let actual = parse_with(&arena, "1 + 2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -288,7 +288,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Int("4")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n+ 4");
|
let actual = parse_with(&arena, "3 \n+ 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -305,7 +305,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 4, Star),
|
Located::new(0, 0, 3, 4, Star),
|
||||||
Located::new(1, 1, 2, 3, spaced_int),
|
Located::new(1, 1, 2, 3, spaced_int),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 *\n 4");
|
let actual = parse_with(&arena, "3 *\n 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -322,7 +322,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(1, 1, 2, 3, Int("4")),
|
Located::new(1, 1, 2, 3, Int("4")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -339,7 +339,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 4, 5, Star),
|
Located::new(0, 0, 4, 5, Star),
|
||||||
Located::new(1, 1, 1, 3, spaced_int),
|
Located::new(1, 1, 1, 3, spaced_int),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "12 * # test!\n 92");
|
let actual = parse_with(&arena, "12 * # test!\n 92");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -359,7 +359,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 0, 1, Plus),
|
Located::new(1, 1, 0, 1, Plus),
|
||||||
Located::new(3, 3, 2, 3, spaced_int2),
|
Located::new(3, 3, 2, 3, spaced_int2),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n+ \n\n 4");
|
let actual = parse_with(&arena, "3 \n+ \n\n 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -373,7 +373,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 4, Minus),
|
Located::new(0, 0, 3, 4, Minus),
|
||||||
Located::new(0, 0, 4, 5, Int("5")),
|
Located::new(0, 0, 4, 5, Int("5")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "-12-5");
|
let actual = parse_with(&arena, "-12-5");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -387,7 +387,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 2, 3, Star),
|
Located::new(0, 0, 2, 3, Star),
|
||||||
Located::new(0, 0, 3, 5, Int("11")),
|
Located::new(0, 0, 3, 5, Int("11")),
|
||||||
));
|
));
|
||||||
let expected = Operator(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "10*11");
|
let actual = parse_with(&arena, "10*11");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
@ -404,9 +404,9 @@ mod test_parse {
|
||||||
let outer = arena.alloc((
|
let outer = arena.alloc((
|
||||||
Located::new(0, 0, 0, 2, Int("31")),
|
Located::new(0, 0, 0, 2, Int("31")),
|
||||||
Located::new(0, 0, 2, 3, Star),
|
Located::new(0, 0, 2, 3, Star),
|
||||||
Located::new(0, 0, 3, 9, Operator(inner)),
|
Located::new(0, 0, 3, 9, BinOp(inner)),
|
||||||
));
|
));
|
||||||
let expected = Operator(outer);
|
let expected = BinOp(outer);
|
||||||
let actual = parse_with(&arena, "31*42+534");
|
let actual = parse_with(&arena, "31*42+534");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue