From efb0a8ebb3d5c13bcd10e84061dffcf88b5cd6bd Mon Sep 17 00:00:00 2001 From: Finn Brewer Date: Fri, 15 Nov 2024 23:50:18 -0800 Subject: [PATCH] feat: support prepending to lists --- CHANGELOG.md | 4 +++ src/gleamgen/expression.gleam | 52 ++++++++++++++++++++++----- test/gleamgen_test.gleam | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6b496b..a84dab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- added support for prepending to lists [x, ..xs] + ## v0.2.1 - 2024-11-13 - added support for or (|) in case expressions diff --git a/src/gleamgen/expression.gleam b/src/gleamgen/expression.gleam index 1ba6fc0..9bc0a9e 100644 --- a/src/gleamgen/expression.gleam +++ b/src/gleamgen/expression.gleam @@ -20,7 +20,10 @@ type InternalExpression(type_) { StrLiteral(String) BoolLiteral(Bool) NilLiteral - ListLiteral(List(Expression(types.Unchecked))) + ListLiteral( + prepending: List(Expression(types.Unchecked)), + initial: Option(Expression(types.Unchecked)), + ) TupleLiteral(List(Expression(types.Unchecked))) Ident(String) Todo(Option(String)) @@ -59,7 +62,7 @@ pub fn nil() -> Expression(Nil) { pub fn list(value: List(Expression(t))) -> Expression(List(t)) { Expression( - ListLiteral(value |> list.map(to_unchecked)), + ListLiteral(value |> list.map(to_unchecked), None), value |> list.first() |> result.map(type_) @@ -68,6 +71,23 @@ pub fn list(value: List(Expression(t))) -> Expression(List(t)) { ) } +pub fn list_prepend( + prepending: List(Expression(t)), + original: Expression(List(t)), +) -> Expression(List(t)) { + Expression( + ListLiteral( + prepending: prepending |> list.map(to_unchecked), + initial: Some(original |> to_unchecked()), + ), + prepending + |> list.first() + |> result.map(type_) + |> result.lazy_unwrap(fn() { types.unchecked() }) + |> types.list(), + ) +} + pub fn tuple1(arg1: Expression(a)) -> Expression(#(a)) { Expression(TupleLiteral([arg1 |> to_unchecked()]), types.tuple1(type_(arg1))) } @@ -641,7 +661,8 @@ pub fn render( BoolLiteral(True) -> doc.from_string("True") BoolLiteral(False) -> doc.from_string("False") NilLiteral -> doc.from_string("Nil") - ListLiteral(values) -> render_list(values, context) + ListLiteral(values, initial_list) -> + render_list(values, initial_list, context) TupleLiteral(values) -> render_tuple(values, context) Ident(value) -> doc.from_string(value) Todo(as_string) -> render_panicking_expression("todo", as_string) @@ -722,19 +743,32 @@ fn render_tuple(values, context) { |> doc.prepend(doc.from_string("#")) } -fn render_list(values, context) { +fn render_list(values, initial_list, context) { let comma = doc.concat([doc.from_string(","), doc.space]) - let trailing_comma = doc.break("", ",") - let open_paren = doc.concat([doc.from_string("["), doc.soft_break]) - let close_paren = doc.concat([trailing_comma, doc.from_string("]")]) + let #(ending, trailing_comma) = case initial_list { + Some(initial) -> #( + doc.concat([ + doc.from_string(","), + doc.space, + doc.from_string(".."), + render(initial, context).doc, + ]), + doc.soft_break, + ) + None -> #(doc.empty, doc.break("", ",")) + } + + let open_bracket = doc.concat([doc.from_string("["), doc.soft_break]) + let close_bracket = doc.concat([trailing_comma, doc.from_string("]")]) values |> list.map(fn(x) { render(x, context).doc }) |> doc.join(with: comma) - |> doc.prepend(open_paren) + |> doc.prepend(open_bracket) + |> doc.append(ending) |> doc.nest(2) - |> doc.append(close_paren) + |> doc.append(close_bracket) |> doc.group } diff --git a/test/gleamgen_test.gleam b/test/gleamgen_test.gleam index fbdb525..5f9e04d 100644 --- a/test/gleamgen_test.gleam +++ b/test/gleamgen_test.gleam @@ -48,6 +48,74 @@ pub fn simple_tuple_test() { |> should.equal("#(3, \"hello\", True)") } +pub fn simple_list_test() { + expression.list([expression.string("hello"), expression.string("hi")]) + |> expression.render(render.default_context()) + |> render.to_string() + |> should.equal("[\"hello\", \"hi\"]") +} + +pub fn simple_list_prepending_test() { + expression.list_prepend( + [expression.string("hello"), expression.string("hi")], + expression.list([expression.string("yo")]), + ) + |> expression.render(render.default_context()) + |> render.to_string() + |> should.equal("[\"hello\", \"hi\", ..[\"yo\"]]") +} + +pub fn multiline_list_prepending_test() { + expression.list_prepend( + [ + expression.string("hello but much, much, much, much longer so it breaks"), + expression.string("hello but much, much, much, much longer so it breaks"), + ], + expression.list([expression.string("yo")]), + ) + |> expression.render(render.default_context()) + |> render.to_string() + |> should.equal( + "[ + \"hello but much, much, much, much longer so it breaks\", + \"hello but much, much, much, much longer so it breaks\", + ..[\"yo\"] +]", + ) +} + +pub fn long_list_test() { + expression.list([ + expression.list([ + expression.string("hello"), + expression.string("hello"), + expression.string("hello"), + expression.string("hello"), + expression.string("hello but much, much, much, much longer so it breaks"), + ]), + expression.list([ + expression.string("hi"), + expression.string("hi"), + expression.string("hi"), + expression.string("hi"), + ]), + ]) + |> expression.render(render.default_context()) + |> render.to_string() + |> should.equal( + "[ + [ + \"hello\", + \"hello\", + \"hello\", + \"hello\", + \"hello but much, much, much, much longer so it breaks\", + ], + [\"hi\", \"hi\", \"hi\", \"hi\"], +]", + ) +} + pub fn long_tuple_test() { expression.tuple9( expression.int(3),