Add lists

This commit is contained in:
Richard Feldman 2019-08-28 00:04:08 -04:00
parent 46750ae6ca
commit 8985b4bec7
9 changed files with 200 additions and 12 deletions

View file

@ -18,6 +18,8 @@ pub enum Expr {
EmptyStr,
Str(String),
Char(char),
List(Vec<Located<Expr>>),
EmptyList,
// Lookups
Var(Symbol),
@ -304,6 +306,24 @@ fn canonicalize(
expr::Expr::Str(string) => ( Str(string), Output::new()),
expr::Expr::Char(ch) => ( Char(ch), Output::new()),
expr::Expr::EmptyStr => ( EmptyStr, Output::new()),
expr::Expr::EmptyList => ( EmptyList, Output::new()),
expr::Expr::List(elems) => {
let mut output = Output::new();
let mut can_elems = Vec::with_capacity(elems.len());
for loc_elem in elems {
let ( can_expr, elem_out ) = canonicalize(env, scope, loc_elem);
output.references = output.references.union(elem_out.references);
can_elems.push(can_expr);
}
// A list literal is never a tail call!
output.tail_call = None;
( List(can_elems), output )
},
expr::Expr::If(loc_cond, loc_true, loc_false) => {
// Canonicalize the nested expressions

View file

@ -34,10 +34,16 @@ pub fn constrain(
EmptyStr => { Eq(string(), expected, region) },
InterpolatedStr(_, _) => { Eq(string(), expected, region) },
EmptyRecord => { Eq(EmptyRec, expected, region) },
EmptyList => { Eq(empty_list(subs.mk_flex_var()), expected, region) },
List(elems) => { list(elems, bound_vars.clone(), subs, expected, region) },
_ => { panic!("TODO constraints") }
}
}
fn empty_list(var: Variable) -> Type {
builtin_type("List", "List", vec![Type::Variable(var)])
}
fn string() -> Type {
builtin_type("String", "String", Vec::new())
}
@ -46,6 +52,34 @@ fn num(var: Variable) -> Type {
builtin_type("Num", "Num", vec![Type::Variable(var)])
}
fn list(loc_elems: Vec<Located<Expr>>, bound_vars: BoundTypeVars, subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
let list_var = subs.mk_flex_var(); // `v` in the type (List v)
let list_type = Type::Variable(list_var);
let mut constraints = Vec::with_capacity(1 + (loc_elems.len() * 2));
for loc_elem in loc_elems {
let elem_var = subs.mk_flex_var();
let elem_type = Variable(elem_var);
let elem_expected = NoExpectation(elem_type.clone());
let elem_constraint = constrain(bound_vars.clone(), subs, loc_elem, elem_expected);
let list_elem_constraint =
Eq(
list_type.clone(),
ForReason(Reason::ElemInList, elem_type, region.clone()),
region.clone()
);
constraints.push(elem_constraint);
constraints.push(list_elem_constraint);
}
constraints.push(
Eq(builtin_type("List", "List", vec![list_type]), expected, region)
);
And(constraints)
}
fn fractional(subs: &mut Subs, expected: Expected<Type>, region: Region) -> Constraint {
// We'll make a Num var1 and a Fractional var2,
// and then add a constraint that var1 needs to equal Fractional var2

View file

@ -11,6 +11,8 @@ pub enum Expr {
EmptyStr,
Str(String),
Char(char),
List(Vec<Located<Expr>>),
EmptyList,
// Lookups
Var(Ident),
@ -113,7 +115,15 @@ impl Expr {
let transformed = transform(self);
match transformed {
Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | InterpolatedStr(_, _) => transformed,
Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | InterpolatedStr(_, _) | EmptyList => transformed,
List(elems) => {
let new_elems =
elems.into_iter()
.map(|loc_elem| loc_elem.with_value(loc_elem.value.walk(transform)))
.collect();
List(new_elems)
}
Assign(assignments, loc_ret) => {
Assign(
assignments.into_iter().map(|(pattern, loc_expr)|

View file

@ -6,7 +6,7 @@ use std::char;
use parse_state::{IndentablePosition};
use combine::parser::char::{char, string, spaces, digit, hex_digit, HexDigit, alpha_num};
use combine::parser::repeat::{many, count_min_max, sep_by1, skip_many, skip_many1, skip_until};
use combine::parser::repeat::{many, count_min_max, sep_by, sep_by1, skip_many, skip_many1, skip_until};
use combine::parser::item::{any, satisfy_map, value, position, satisfy};
use combine::parser::combinator::{look_ahead, not_followed_by};
use combine::{attempt, choice, eof, many1, parser, Parser, optional, between, unexpected_any, unexpected};
@ -205,6 +205,7 @@ parser! {
choice((
closure(min_indent),
apply_with_parens(min_indent),
list(min_indent),
string("{}").with(value(Expr::EmptyRecord)),
string_literal(),
int_or_frac_literal(),
@ -237,7 +238,6 @@ parser! {
located(choice((
function_arg_expr(min_indent),
apply_with_parens(min_indent),
assignment(min_indent),
apply_variant(min_indent),
func_or_var(min_indent),
@ -312,6 +312,27 @@ where I: Stream<Item = char, Position = IndentablePosition>,
)
}
pub fn list<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
{
between(char('['), char(']'),
sep_by(
indented_whitespaces(min_indent)
.with(located(expr_body(min_indent)))
.skip(indented_whitespaces(min_indent)),
char(',')
)
).map(|loc_elems: Vec<Located<Expr>>| {
if loc_elems.is_empty() {
Expr::EmptyList
} else {
Expr::List(loc_elems)
}
})
}
pub fn apply_with_parens<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position>
@ -322,7 +343,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
.skip(indented_whitespaces(min_indent))
).and(
// Parenthetical expressions can optionally be followed by
// whitespace and one or more comma-separated expressions,
// whitespace and one or more whitespace-separated expressions,
// meaning this is function application!
optional(
attempt(apply_args(min_indent))

View file

@ -29,7 +29,9 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_p
match flat_type {
Apply(module_name, type_name, vars) => {
if use_parens {
let write_parens = use_parens && !vars.is_empty();
if write_parens {
buf.push_str("(");
}
@ -40,7 +42,7 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_p
write_content(subs.get(var).content, subs, buf, true);
}
if use_parens {
if write_parens {
buf.push_str(")");
}
},

View file

@ -40,7 +40,8 @@ impl<T> Expected<T> {
pub enum Reason {
OperatorLeftArg(Operator),
OperatorRightArg(Operator),
FractionalLiteral
FractionalLiteral,
ElemInList,
}
#[derive(Debug)]

View file

@ -38,8 +38,16 @@ pub fn zero_loc_expr(expr: Expr) -> Expr {
use roc::expr::Expr::*;
match expr {
Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord => expr,
Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | EmptyList => expr,
InterpolatedStr(pairs, string) => InterpolatedStr(pairs.into_iter().map(|( prefix, ident )| ( prefix, zero_loc(ident))).collect(), string),
List(elems) => {
let zeroed_elems =
elems.into_iter().map(|loc_expr|
loc(zero_loc_expr(loc_expr.value))
).collect();
List(zeroed_elems)
},
Assign(assignments, loc_ret) => {
let zeroed_assignments =
assignments.into_iter().map(|( pattern, loc_expr )|

View file

@ -102,11 +102,70 @@ mod test_infer {
);
}
// LIST
#[test]
fn infer_empty_list() {
infer_eq(
indoc!(r#"
[]
"#),
"List.List *"
);
}
#[test]
fn infer_list_of_one_num() {
infer_eq(
indoc!(r#"
[42]
"#),
"List.List (Num.Num *)"
);
}
#[test]
fn infer_list_of_nums() {
infer_eq(
indoc!(r#"
[ 1, 2, 3 ]
"#),
"List.List (Num.Num *)"
);
}
#[test]
fn infer_list_of_one_string() {
infer_eq(
indoc!(r#"
[ "cowabunga" ]
"#),
"List.List String.String"
);
}
#[test]
fn infer_list_of_strings() {
infer_eq(
indoc!(r#"
[ "foo", "bar" ]
"#),
"List.List String.String"
);
}
// #[test]
// fn infer_interpolated_string() {
// assert_eq!(
// infer_expr(&Expr::InterpolatedStr(vec![], "type inference!".to_string()), MutMap::default()),
// Builtin(Str)
// infer_eq(
// indoc!(r#"
// whatItIs = "great"
// "type inference is \(whatItIs)!"
// "#),
// "String.String"
// );
// }

View file

@ -44,6 +44,39 @@ mod test_parse {
)
}
// LIST LITERALS
#[test]
fn empty_list() {
assert_fully_parses(
indoc!(r#"
[]
"#),
EmptyList
);
}
#[test]
fn single_list() {
assert_fully_parses(
indoc!(r#"
[ 1 ]
"#),
List(vec![loc(Int(1))])
);
}
#[test]
fn multi_list() {
assert_fully_parses(
indoc!(r#"
[1 , 2,3]
"#),
List(vec![loc(Int(1)), loc(Int(2)), loc(Int(3))])
);
}
// STRING LITERALS
fn expect_parsed_str<'a>(expected_str: &'a str, actual_str: &'a str) {