mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 23:31:12 +00:00
Add lists
This commit is contained in:
parent
46750ae6ca
commit
8985b4bec7
9 changed files with 200 additions and 12 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
12
src/expr.rs
12
src/expr.rs
|
@ -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)|
|
||||
|
|
27
src/parse.rs
27
src/parse.rs
|
@ -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))
|
||||
|
|
|
@ -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(")");
|
||||
}
|
||||
},
|
||||
|
|
|
@ -40,7 +40,8 @@ impl<T> Expected<T> {
|
|||
pub enum Reason {
|
||||
OperatorLeftArg(Operator),
|
||||
OperatorRightArg(Operator),
|
||||
FractionalLiteral
|
||||
FractionalLiteral,
|
||||
ElemInList,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -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 )|
|
||||
|
|
|
@ -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"
|
||||
// );
|
||||
// }
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue