Skip to content

Commit

Permalink
Map implicit to explicit returns
Browse files Browse the repository at this point in the history
  • Loading branch information
dusty-phillips committed Aug 21, 2024
1 parent 66861e8 commit 1674d9a
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 60 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ PRs are welcome.
- 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
- implicit returns from functions aren't hooked up
- no destructuring/pattern matching in let
- no let assert
- label aliases aren't supported yet (ie `fn foo(bar bas: Str)`)
- haven't really tested with nesting of expressions
- need to print out nice errors when glance fails to parse
Expand Down
3 changes: 3 additions & 0 deletions src/generator.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ fn generate_statement(statement: python.Statement) -> StringBuilder {
python.Expression(expression) ->
generate_expression(expression)
|> generator_helpers.append_if_not_empty("\n")
python.Return(expression) ->
string_builder.from_string("return ")
|> string_builder.append_builder(generate_expression(expression))
python.SimpleAssignment(name, value) -> {
string_builder.new()
|> string_builder.append(name)
Expand Down
12 changes: 12 additions & 0 deletions src/internal/transformer.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import gleam/list

pub fn transform_last(elements: List(a), transformer: fn(a) -> a) -> List(a) {
// This makes three iterations over elements. It may be a candidate for optimization
// since it happens on all the statements in every function body. I can find ways
// to do it with only two iterations, or with one iteration but transforming every
// element and discarding the intermediates, but I didn't have any luck with a solution
// that could do it in only one iteration.
let length = list.length(elements)
let #(head, tail) = list.split(elements, length - 1)
list.append(head, tail |> list.map(transformer))
}
1 change: 1 addition & 0 deletions src/python.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub type Expression {

pub type Statement {
Expression(Expression)
Return(Expression)
SimpleAssignment(name: String, value: Expression)
}

Expand Down
15 changes: 14 additions & 1 deletion src/transformer.gleam
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import glance
import gleam/list
import gleam/option
import internal/transformer
import pprint
import python

Expand All @@ -20,6 +21,17 @@ fn maybe_extract_external(
}
}

fn add_return_if_returnable_expression(
statement: python.Statement,
) -> python.Statement {
case statement {
python.Expression(python.Panic(_)) -> statement
python.Expression(python.Todo(_)) -> statement
python.Expression(expr) -> python.Return(expr)
statement -> statement
}
}

fn transform_function_parameter(
function_parameter: glance.FunctionParameter,
) -> python.FunctionParameter {
Expand Down Expand Up @@ -201,7 +213,8 @@ fn transform_function(function: glance.Function) -> python.Function {
python.Function(
name: function.name,
parameters: list.map(function.parameters, transform_function_parameter),
body: list.map(function.body, transform_statement),
body: list.map(function.body, transform_statement)
|> transformer.transform_last(add_return_if_returnable_expression),
)
}

Expand Down
87 changes: 29 additions & 58 deletions test/expression_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ pub fn string_expression_test() {
"from gleam_builtins import *
def main():
\"bar\"
",
return \"bar\"",
)
}

Expand All @@ -26,8 +25,7 @@ pub fn int_expression_test() {
"from gleam_builtins import *
def main():
42
",
return 42",
)
}

Expand All @@ -41,8 +39,7 @@ pub fn float_expression_test() {
"from gleam_builtins import *
def main():
12.5
",
return 12.5",
)
}

Expand All @@ -56,8 +53,7 @@ pub fn tuple_expression_test() {
"from gleam_builtins import *
def main():
(42, 12.5, \"foo\")
",
return (42, 12.5, \"foo\")",
)
}

Expand All @@ -71,8 +67,7 @@ pub fn true_expression_test() {
"from gleam_builtins import *
def main():
True
",
return True",
)
}

Expand All @@ -86,8 +81,7 @@ pub fn false_expression_test() {
"from gleam_builtins import *
def main():
False
",
return False",
)
}

Expand All @@ -101,8 +95,7 @@ pub fn variable_expression_test() {
"from gleam_builtins import *
def main():
println(a)
",
return println(a)",
)
}

Expand Down Expand Up @@ -206,8 +199,7 @@ pub fn tuple_index_test() {
"from gleam_builtins import *
def main():
(42, 12.5, \"foo\")[1]
",
return (42, 12.5, \"foo\")[1]",
)
}

Expand All @@ -221,8 +213,7 @@ pub fn field_access_test() {
"from gleam_builtins import *
def main():
foo.b
",
return foo.b",
)
}

Expand All @@ -236,8 +227,7 @@ pub fn binop_int_add_test() {
"from gleam_builtins import *
def main():
40 + 2
",
return 40 + 2",
)
}

Expand All @@ -251,8 +241,7 @@ pub fn binop_float_add_test() {
"from gleam_builtins import *
def main():
40.2 + 2.5
",
return 40.2 + 2.5",
)
}

Expand All @@ -266,8 +255,7 @@ pub fn binop_concat_add_test() {
"from gleam_builtins import *
def main():
\"hello \" + \"world\"
",
return \"hello \" + \"world\"",
)
}

Expand All @@ -281,8 +269,7 @@ pub fn binop_int_sub_test() {
"from gleam_builtins import *
def main():
40 - 2
",
return 40 - 2",
)
}

Expand All @@ -296,8 +283,7 @@ pub fn binop_float_sub_test() {
"from gleam_builtins import *
def main():
40.2 - 2.5
",
return 40.2 - 2.5",
)
}

Expand All @@ -311,8 +297,7 @@ pub fn binop_int_div_test() {
"from gleam_builtins import *
def main():
40 // 2
",
return 40 // 2",
)
}

Expand All @@ -326,8 +311,7 @@ pub fn binop_float_div_test() {
"from gleam_builtins import *
def main():
40.2 / 2.5
",
return 40.2 / 2.5",
)
}

Expand All @@ -341,8 +325,7 @@ pub fn binop_int_modulo_test() {
"from gleam_builtins import *
def main():
5 % 2
",
return 5 % 2",
)
}

Expand All @@ -356,8 +339,7 @@ pub fn equality_test() {
"from gleam_builtins import *
def main():
5 == 5
",
return 5 == 5",
)
}

Expand All @@ -371,8 +353,7 @@ pub fn inequality_test() {
"from gleam_builtins import *
def main():
5 != 2
",
return 5 != 2",
)
}

Expand All @@ -386,8 +367,7 @@ pub fn lt_int_test() {
"from gleam_builtins import *
def main():
5 < 2
",
return 5 < 2",
)
}

Expand All @@ -401,8 +381,7 @@ pub fn lt_float_test() {
"from gleam_builtins import *
def main():
5.0 < 2.0
",
return 5.0 < 2.0",
)
}

Expand All @@ -416,8 +395,7 @@ pub fn lt_eq_int_test() {
"from gleam_builtins import *
def main():
5 <= 2
",
return 5 <= 2",
)
}

Expand All @@ -431,8 +409,7 @@ pub fn lt_eq_float_test() {
"from gleam_builtins import *
def main():
5.0 <= 2.0
",
return 5.0 <= 2.0",
)
}

Expand All @@ -446,8 +423,7 @@ pub fn logical_or_test() {
"from gleam_builtins import *
def main():
True or False
",
return True or False",
)
}

Expand All @@ -461,8 +437,7 @@ pub fn logical_and_test() {
"from gleam_builtins import *
def main():
True and False
",
return True and False",
)
}

Expand All @@ -476,8 +451,7 @@ pub fn simple_pipe_test() {
"from gleam_builtins import *
def main():
println(\"foo\")
",
return println(\"foo\")",
)
}

Expand All @@ -491,8 +465,7 @@ pub fn capture_pipe_test() {
"from gleam_builtins import *
def main():
println(\"a\", \"foo\", \"b\")
",
return println(\"a\", \"foo\", \"b\")",
)
}

Expand All @@ -506,8 +479,7 @@ pub fn simple_call_expression_test() {
"from gleam_builtins import *
def main():
foo(\"bar\")
",
return foo(\"bar\")",
)
}

Expand All @@ -521,8 +493,7 @@ pub fn labelled_argument_call_expression_test() {
"from gleam_builtins import *
def main():
foo(\"bar\", baz=\"baz\")
",
return foo(\"bar\", baz=\"baz\")",
)
}

Expand Down
14 changes: 14 additions & 0 deletions test/function_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,17 @@ def func2():
pass",
)
}

pub fn function_with_return_value_test() {
"fn greet() -> String {
\"hello world\"
}"
|> macabre.compile
|> should.be_ok
|> should.equal(
"from gleam_builtins import *
def greet():
return \"hello world\"",
)
}
Loading

0 comments on commit 1674d9a

Please sign in to comment.