Skip to content

Commit 43a299c

Browse files
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.
1 parent b71aac1 commit 43a299c

File tree

5 files changed

+51
-32
lines changed

5 files changed

+51
-32
lines changed

README.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,20 @@ PRs are welcome.
4848
### Some of the things I know are missing
4949

5050
- no case expressions
51+
- block expressions aren't supported yet
52+
- fn expressions are not supported yet
5153
- no destructuring/pattern matching in let
5254
- no let assert
5355
- label aliases aren't supported yet (ie `fn foo(bar bas: Str)`)
5456
- const definitions aren't supported yet (module.constants)
5557
- type aliases aren't supported yet (module.type_aliases)
56-
- block expressions aren't supported yet
57-
- fnCapture and fn expressions are not supported yet
58-
- but fnCapture is supported as part of a Call, so that probably needs to be unwrapped
5958
- haven't really tested with nesting of expressions
6059
- need to print out nice errors when glance fails to parse
6160
- No Result custom types yet
6261
- List custom type is missing `match_args`, other helpers
6362
- glance doesn't support comments
64-
- Not doing anything to avoid collision between gleam identifiers with python keywords
6563
- glance doesn't typecheck (e.g. `2.0 - 1.5` compiles successfully, but should be `2.0 -. 1.5`)
64+
- Not doing anything to avoid collision between gleam identifiers with python keywords
6665
- not currently generating python type hints (e.g. function arguments and return types), but gleam gives us that info so may as well use it
6766
- no concept of a "project", gleam.toml, downloading dependencies
6867
- only compiles one module at a time

src/generator.gleam

+12
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ fn generate_expression(expression: python.Expression) {
150150
))
151151
|> string_builder.append(")")
152152

153+
python.Lambda(arguments, body) -> {
154+
string_builder.from_string("(lambda ")
155+
|> string_builder.append_builder(generator_helpers.generate_plural(
156+
arguments,
157+
generate_expression,
158+
", ",
159+
))
160+
|> string_builder.append(": ")
161+
|> string_builder.append_builder(generate_expression(body))
162+
|> string_builder.append(")")
163+
}
164+
153165
python.Call(function, arguments) ->
154166
string_builder.new()
155167
|> string_builder.append_builder(generate_expression(function))

src/python.gleam

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub type Expression {
4141
Not(Expression)
4242
Panic(Expression)
4343
Todo(Expression)
44+
Lambda(args: List(Expression), body: Expression)
4445
List(elements: List(Expression))
4546
ListWithRest(elements: List(Expression), rest: Expression)
4647
TupleIndex(tuple: Expression, index: Int)

src/transformer.gleam

+18-27
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
123123
)
124124
}
125125

126+
glance.FnCapture(label, function, arguments_before, arguments_after) -> {
127+
let argument_expressions =
128+
list.concat([
129+
arguments_before,
130+
[glance.Field(label, glance.Variable("fn_capture"))],
131+
arguments_after,
132+
])
133+
|> list.map(transform_call_argument)
134+
135+
python.Lambda(
136+
[python.Variable("fn_capture")],
137+
python.Call(transform_expression(function), argument_expressions),
138+
)
139+
}
140+
126141
glance.Tuple(expressions) ->
127142
expressions
128143
|> list.map(transform_expression)
@@ -134,35 +149,12 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
134149
glance.FieldAccess(container: expression, label:) ->
135150
python.FieldAccess(transform_expression(expression), label)
136151

137-
glance.BinaryOperator(glance.Pipe, left, glance.Variable(function)) -> {
138-
// simple pipe left |> foo
139-
python.Call(python.Variable(function), [
152+
glance.BinaryOperator(glance.Pipe, left, right) -> {
153+
python.Call(transform_expression(right), [
140154
python.UnlabelledField(transform_expression(left)),
141155
])
142156
}
143157

144-
glance.BinaryOperator(
145-
glance.Pipe,
146-
left,
147-
glance.FnCapture(
148-
label,
149-
glance.Variable(function),
150-
arguments_before,
151-
arguments_after,
152-
),
153-
) -> {
154-
let argument_expressions =
155-
list.concat([
156-
arguments_before,
157-
[glance.Field(label, left)],
158-
arguments_after,
159-
])
160-
|> list.map(transform_call_argument)
161-
python.Call(python.Variable(function), argument_expressions)
162-
}
163-
glance.BinaryOperator(glance.Pipe, _, right) -> {
164-
panic as "I don't know how to handle this structure of pipe"
165-
}
166158
glance.BinaryOperator(name, left, right) ->
167159
transform_binop(name, left, right)
168160

@@ -187,8 +179,7 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
187179
glance.BitString(_) as expr
188180
| glance.Block(_) as expr
189181
| glance.Case(_, _) as expr
190-
| glance.Fn(_, _, _) as expr
191-
| glance.FnCapture(_, _, _, _) as expr -> {
182+
| glance.Fn(_, _, _) as expr -> {
192183
pprint.debug(expr)
193184
todo as "Several expressions are not implemented yet"
194185
}

test/expression_test.gleam

+17-1
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ pub fn capture_pipe_test() {
507507
"from gleam_builtins import *
508508
509509
def main():
510-
return println(\"a\", \"foo\", \"b\")",
510+
return (lambda fn_capture: println(\"a\", fn_capture, \"b\"))(\"foo\")",
511511
)
512512
}
513513

@@ -539,6 +539,22 @@ def main():
539539
)
540540
}
541541

542+
pub fn fn_capture_test() {
543+
"fn main() {
544+
let x = foo(\"a\", _, \"b\")
545+
x(\"c\")
546+
}"
547+
|> macabre.compile
548+
|> should.be_ok
549+
|> should.equal(
550+
"from gleam_builtins import *
551+
552+
def main():
553+
x = (lambda fn_capture: foo(\"a\", fn_capture, \"b\"))
554+
return x(\"c\")",
555+
)
556+
}
557+
542558
pub fn record_update_test() {
543559
"pub type Foo {
544560
Bar(a: Int, b: String)

0 commit comments

Comments
 (0)