Skip to content

Commit

Permalink
Support fn_capture as lambda.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dusty-phillips committed Aug 21, 2024
1 parent b71aac1 commit c3f3f73
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 35 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,29 @@ PRs are welcome.
### Some of the things I know are missing

- no case expressions
- block expressions aren't supported yet
- fn expressions are not supported yet
- no destructuring/pattern matching in let
- no let assert
- label aliases aren't supported yet (ie `fn foo(bar bas: Str)`)
- const definitions aren't supported yet (module.constants)
- type aliases aren't supported yet (module.type_aliases)
- block expressions aren't supported yet
- fnCapture and fn expressions are not supported yet
- but fnCapture is supported as part of a Call, so that probably needs to be unwrapped
- haven't really tested with nesting of expressions
- need to print out nice errors when glance fails to parse
- bitstrings aren't supported yet (map to bytes)
- No Result custom types yet
- List custom type is missing `match_args`, other helpers
- glance doesn't support comments
- Not doing anything to avoid collision between gleam identifiers with python keywords
- glance doesn't typecheck (e.g. `2.0 - 1.5` compiles successfully, but should be `2.0 -. 1.5`)
- Not doing anything to avoid collision between gleam identifiers with python keywords
- 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
- haven't really tested with nesting of expressions
- need to print out nice errors when glance fails to parse
- no concept of a "project", gleam.toml, downloading dependencies
- only compiles one module at a time
- copies the prelude module blindly into the directory that contains that one module
- eliminate all todos in source code
- No standard library
- generate **main** if a module has a main function
- calling functions or constructors with out of order positional args doesn't work in python
- calling functions or constructors with out-of-order positional args doesn't work in python
- e.g. `Foo(mystr: String, point: #(Int, Int))` can be called with `Foo(#(1, 1), mystr: "Foo")` in gleam
- javascript seems to solve this by automatically reordering the arguments to match the input type
- custom types with unlabelled fields are not working
Expand Down
12 changes: 12 additions & 0 deletions src/generator.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions src/python.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
45 changes: 18 additions & 27 deletions src/transformer.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand All @@ -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"
}
Expand Down
18 changes: 17 additions & 1 deletion test/expression_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -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\")",
)
}

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit c3f3f73

Please sign in to comment.