From c5c602df948830a44ae8dce3c4e7d5c6889fd9be Mon Sep 17 00:00:00 2001 From: Dusty Phillips Date: Wed, 21 Aug 2024 13:50:33 -0300 Subject: [PATCH] Support fn_capture as lambda. I think it's safe to make it always a lambda. Python expressions never cross line boundaries, including in Lambdas. In general, this means that not all gleam expressions can map to python expressions (consider case->match, where match is a statement in python, not an expression). But I *think* it means that any expression that can map to a python expression can be wrapped in a lambda like this. The parens make it so the expression is self-contained and you can call it. --- src/generator.gleam | 12 ++++++++++ src/python.gleam | 1 + src/transformer.gleam | 45 +++++++++++++++----------------------- test/expression_test.gleam | 18 ++++++++++++++- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/generator.gleam b/src/generator.gleam index d5048ed..6110416 100644 --- a/src/generator.gleam +++ b/src/generator.gleam @@ -150,6 +150,18 @@ fn generate_expression(expression: python.Expression) { )) |> string_builder.append(")") + python.Lambda(arguments, body) -> { + string_builder.from_string("(lambda ") + |> string_builder.append_builder(generator_helpers.generate_plural( + arguments, + generate_expression, + ", ", + )) + |> string_builder.append(": ") + |> string_builder.append_builder(generate_expression(body)) + |> string_builder.append(")") + } + python.Call(function, arguments) -> string_builder.new() |> string_builder.append_builder(generate_expression(function)) diff --git a/src/python.gleam b/src/python.gleam index a573ab5..27c6c1b 100644 --- a/src/python.gleam +++ b/src/python.gleam @@ -41,6 +41,7 @@ pub type Expression { Not(Expression) Panic(Expression) Todo(Expression) + Lambda(args: List(Expression), body: Expression) List(elements: List(Expression)) ListWithRest(elements: List(Expression), rest: Expression) TupleIndex(tuple: Expression, index: Int) diff --git a/src/transformer.gleam b/src/transformer.gleam index e6795c1..1e9e1a0 100644 --- a/src/transformer.gleam +++ b/src/transformer.gleam @@ -123,6 +123,21 @@ fn transform_expression(expression: glance.Expression) -> python.Expression { ) } + glance.FnCapture(label, function, arguments_before, arguments_after) -> { + let argument_expressions = + list.concat([ + arguments_before, + [glance.Field(label, glance.Variable("fn_capture"))], + arguments_after, + ]) + |> list.map(transform_call_argument) + + python.Lambda( + [python.Variable("fn_capture")], + python.Call(transform_expression(function), argument_expressions), + ) + } + glance.Tuple(expressions) -> expressions |> list.map(transform_expression) @@ -134,35 +149,12 @@ fn transform_expression(expression: glance.Expression) -> python.Expression { glance.FieldAccess(container: expression, label:) -> python.FieldAccess(transform_expression(expression), label) - glance.BinaryOperator(glance.Pipe, left, glance.Variable(function)) -> { - // simple pipe left |> foo - python.Call(python.Variable(function), [ + glance.BinaryOperator(glance.Pipe, left, right) -> { + python.Call(transform_expression(right), [ python.UnlabelledField(transform_expression(left)), ]) } - glance.BinaryOperator( - glance.Pipe, - left, - glance.FnCapture( - label, - glance.Variable(function), - arguments_before, - arguments_after, - ), - ) -> { - let argument_expressions = - list.concat([ - arguments_before, - [glance.Field(label, left)], - arguments_after, - ]) - |> list.map(transform_call_argument) - python.Call(python.Variable(function), argument_expressions) - } - glance.BinaryOperator(glance.Pipe, _, right) -> { - panic as "I don't know how to handle this structure of pipe" - } glance.BinaryOperator(name, left, right) -> transform_binop(name, left, right) @@ -187,8 +179,7 @@ fn transform_expression(expression: glance.Expression) -> python.Expression { glance.BitString(_) as expr | glance.Block(_) as expr | glance.Case(_, _) as expr - | glance.Fn(_, _, _) as expr - | glance.FnCapture(_, _, _, _) as expr -> { + | glance.Fn(_, _, _) as expr -> { pprint.debug(expr) todo as "Several expressions are not implemented yet" } diff --git a/test/expression_test.gleam b/test/expression_test.gleam index 1cd4fea..832a372 100644 --- a/test/expression_test.gleam +++ b/test/expression_test.gleam @@ -507,7 +507,7 @@ pub fn capture_pipe_test() { "from gleam_builtins import * def main(): - return println(\"a\", \"foo\", \"b\")", + return (lambda fn_capture: println(\"a\", fn_capture, \"b\"))(\"foo\")", ) } @@ -539,6 +539,22 @@ def main(): ) } +pub fn fn_capture_test() { + "fn main() { + let x = foo(\"a\", _, \"b\") + x(\"c\") + }" + |> macabre.compile + |> should.be_ok + |> should.equal( + "from gleam_builtins import * + +def main(): + x = (lambda fn_capture: foo(\"a\", fn_capture, \"b\")) + return x(\"c\")", + ) +} + pub fn record_update_test() { "pub type Foo { Bar(a: Int, b: String)