Skip to content

Commit

Permalink
Parallel list comprehension (#1673)
Browse files Browse the repository at this point in the history
Close #1674.
  • Loading branch information
raviqqe authored Dec 26, 2022
1 parent 0136c27 commit b3c28b5
Show file tree
Hide file tree
Showing 26 changed files with 1,412 additions and 686 deletions.
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"indoc",
"insta",
"intrinsics",
"iteratees",
"itertools",
"koka",
"lcov",
Expand Down
10 changes: 10 additions & 0 deletions features/types/list.feature
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ Feature: List
When I run `pen build`
Then the exit status should be 0

Scenario: Zip lists
Given a file named "Foo.pen" with:
"""pen
f = \(xs [number], ys [number]) [number] {
[number x() + y() for x, y in xs, ys]
}
"""
When I run `pen build`
Then the exit status should be 0

Scenario: Get a size of a list
Given a file named "Foo.pen" with:
"""pen
Expand Down
57 changes: 32 additions & 25 deletions lib/ast-hir/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,17 @@ fn compile_expression(expression: &ast::Expression) -> Result<ir::Expression, Co
.rev()
.map(|branch| {
Ok(ir::ListComprehensionBranch::new(
None,
branch.primary_name(),
branch.secondary_name().map(String::from),
compile_expression(branch.iteratee())?,
branch.names().to_vec(),
branch
.iteratees()
.iter()
.map(|iteratee| {
Ok(ir::ListComprehensionIteratee::new(
None,
compile_expression(iteratee)?,
))
})
.collect::<Result<_, _>>()?,
branch.condition().map(compile_expression).transpose()?,
branch.position().clone(),
))
Expand Down Expand Up @@ -497,16 +504,14 @@ mod tests {
ast::Variable::new("x", Position::fake()),
vec![
ast::ListComprehensionBranch::new(
"y",
None,
ast::Variable::new("x", Position::fake()),
vec!["y".into()],
vec![ast::Variable::new("x", Position::fake()).into()],
None,
Position::fake(),
),
ast::ListComprehensionBranch::new(
"x",
None,
ast::Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![ast::Variable::new("xs", Position::fake()).into()],
None,
Position::fake(),
),
Expand Down Expand Up @@ -534,18 +539,20 @@ mod tests {
ir::Variable::new("x", Position::fake()),
vec![
ir::ListComprehensionBranch::new(
None,
"x",
None,
ir::Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![ir::ListComprehensionIteratee::new(
None,
ir::Variable::new("xs", Position::fake()),
)],
None,
Position::fake(),
),
ir::ListComprehensionBranch::new(
None,
"y",
None,
ir::Variable::new("x", Position::fake()),
vec!["y".into()],
vec![ir::ListComprehensionIteratee::new(
None,
ir::Variable::new("x", Position::fake()),
)],
None,
Position::fake(),
),
Expand Down Expand Up @@ -580,9 +587,8 @@ mod tests {
ast::types::Reference::new("number", Position::fake()),
ast::Variable::new("x", Position::fake()),
vec![ast::ListComprehensionBranch::new(
"x",
None,
ast::Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![ast::Variable::new("xs", Position::fake()).into()],
Some(ast::Variable::new("true", Position::fake()).into()),
Position::fake(),
),],
Expand All @@ -608,10 +614,11 @@ mod tests {
types::Reference::new("number", Position::fake()),
ir::Variable::new("x", Position::fake()),
vec![ir::ListComprehensionBranch::new(
None,
"x",
None,
ir::Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![ir::ListComprehensionIteratee::new(
None,
ir::Variable::new("xs", Position::fake()),
)],
Some(ir::Variable::new("true", Position::fake()).into()),
Position::fake(),
)],
Expand Down
27 changes: 10 additions & 17 deletions lib/ast/src/ast/list_comprehension_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,33 @@ use std::rc::Rc;

#[derive(Clone, Debug, PartialEq)]
pub struct ListComprehensionBranch {
primary_name: String,
secondary_name: Option<String>,
iteratee: Rc<Expression>,
names: Vec<String>,
iteratees: Vec<Expression>,
condition: Option<Rc<Expression>>,
position: Position,
}

impl ListComprehensionBranch {
pub fn new(
primary_name: impl Into<String>,
secondary_name: Option<String>,
iteratee: impl Into<Expression>,
names: Vec<String>,
iteratees: Vec<Expression>,
condition: Option<Expression>,
position: Position,
) -> Self {
Self {
primary_name: primary_name.into(),
secondary_name,
iteratee: iteratee.into().into(),
names,
iteratees,
condition: condition.map(From::from),
position,
}
}

pub fn primary_name(&self) -> &str {
&self.primary_name
pub fn names(&self) -> &[String] {
&self.names
}

pub fn secondary_name(&self) -> Option<&str> {
self.secondary_name.as_deref()
}

pub fn iteratee(&self) -> &Expression {
&self.iteratee
pub fn iteratees(&self) -> &[Expression] {
&self.iteratees
}

pub fn condition(&self) -> Option<&Expression> {
Expand Down
81 changes: 58 additions & 23 deletions lib/format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,18 +478,26 @@ fn compile_expression(context: &mut Context, expression: &Expression) -> Documen
.map(|branch| {
compile_line_comment(context, branch.position(), |context| {
sequence(
["for ".into(), branch.primary_name().into()]
["for ".into()]
.into_iter()
.chain(
branch
.secondary_name()
.into_iter()
.flat_map(|name| [", ".into(), name.into()]),
.names()
.iter()
.map(|string| string.as_str().into())
.intersperse(", ".into()),
)
.chain([" in ".into()])
.chain(
branch
.iteratees()
.iter()
.map(|iteratee| {
compile_expression(context, iteratee)
})
.intersperse(", ".into())
.collect::<Vec<_>>(),
)
.chain([
" in ".into(),
compile_expression(context, branch.iteratee()),
])
.chain(branch.condition().map(|condition| {
sequence([
" if ".into(),
Expand Down Expand Up @@ -3107,9 +3115,8 @@ mod tests {
types::Reference::new("none", Position::fake()),
Variable::new("none", Position::fake()),
vec![ListComprehensionBranch::new(
"x",
None,
Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![Variable::new("xs", Position::fake()).into()],
None,
Position::fake(),
)],
Expand All @@ -3129,9 +3136,8 @@ mod tests {
types::Reference::new("none", Position::fake()),
Variable::new("none", line_position(2)),
vec![ListComprehensionBranch::new(
"x",
None,
Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![Variable::new("xs", Position::fake()).into()],
None,
line_position(2),
)],
Expand Down Expand Up @@ -3159,9 +3165,8 @@ mod tests {
types::Reference::new("none", Position::fake()),
Variable::new("none", line_position(2)),
vec![ListComprehensionBranch::new(
"x",
None,
Variable::new("xs", Position::fake()),
vec!["x".into()],
vec![Variable::new("xs", Position::fake()).into()],
Some(Variable::new("true", Position::fake()).into()),
line_position(2)
)],
Expand All @@ -3180,6 +3185,38 @@ mod tests {
.trim()
);
}

#[test]
fn format_parallel() {
assert_eq!(
format(
&ListComprehension::new(
types::Reference::new("none", Position::fake()),
Variable::new("none", line_position(2)),
vec![ListComprehensionBranch::new(
vec!["x".into(), "y".into()],
vec![
Variable::new("xs", Position::fake()).into(),
Variable::new("ys", Position::fake()).into()
],
None,
line_position(2)
)],
line_position(1)
)
.into()
),
indoc!(
"
[none
none
for x, y in xs, ys
]
"
)
.trim()
);
}
}
}

Expand Down Expand Up @@ -3362,9 +3399,8 @@ mod tests {
types::Reference::new("none", Position::fake()),
Variable::new("none", Position::fake()),
vec![ListComprehensionBranch::new(
"k",
Some("v".into()),
Variable::new("xs", Position::fake()),
vec!["k".into(), "v".into()],
vec![Variable::new("xs", Position::fake()).into()],
None,
Position::fake(),
)],
Expand All @@ -3384,9 +3420,8 @@ mod tests {
types::Reference::new("none", Position::fake()),
Variable::new("none", line_position(2)),
vec![ListComprehensionBranch::new(
"k",
Some("v".into()),
Variable::new("xs", Position::fake()),
vec!["k".into(), "v".into()],
vec![Variable::new("xs", Position::fake()).into()],
None,
line_position(2)
)],
Expand Down
9 changes: 5 additions & 4 deletions lib/hir-mir/src/built_in_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ pub fn compile(
position.clone(),
),
vec![ListComprehensionBranch::new(
Some(types::List::new(list_type, call.position().clone()).into()),
ELEMENT_NAME,
None,
call.arguments()[0].clone(),
vec![ELEMENT_NAME.into()],
vec![ListComprehensionIteratee::new(
Some(types::List::new(list_type, call.position().clone()).into()),
call.arguments()[0].clone(),
)],
None,
position.clone(),
)],
Expand Down
28 changes: 22 additions & 6 deletions lib/hir-mir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub enum CompileError {
InvalidVariantType(Type),
MainFunctionNotFound(Position),
MirTypeCheck(mir::analysis::type_check::TypeCheckError),
MixedIterateesInListComprehension(Position),
MultipleMapsInListComprehension(Position),
NewContextFunctionNotFound(Position),
}

Expand All @@ -33,23 +35,37 @@ impl Display for CompileError {
position
)
}
Self::InvalidVariantType(type_) => {
write!(
formatter,
"unsupported type information: {}\n{}",
type_formatter::format(type_),
type_.position()
)
}
Self::MainFunctionNotFound(position) => {
write!(formatter, "main function not found\n{}", position)
}
Self::MirTypeCheck(error) => {
write!(formatter, "failed to check types in MIR: {}", error)
}
Self::NewContextFunctionNotFound(position) => {
write!(formatter, "new context function not found\n{}", position)
Self::MixedIterateesInListComprehension(position) => {
write!(
formatter,
"mixed iteratee types in list comprehension\n{}",
position
)
}
Self::InvalidVariantType(type_) => {
Self::MultipleMapsInListComprehension(position) => {
write!(
formatter,
"unsupported type information: {}\n{}",
type_formatter::format(type_),
type_.position()
"multiple maps in list comprehension\n{}",
position
)
}
Self::NewContextFunctionNotFound(position) => {
write!(formatter, "new context function not found\n{}", position)
}
}
}
}
Expand Down
Loading

0 comments on commit b3c28b5

Please sign in to comment.