diff --git a/README.md b/README.md index 43ba5c2..adffe6c 100644 --- a/README.md +++ b/README.md @@ -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 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)