Skip to content

Commit 73f7da6

Browse files
Support record update and more flexible calling.
1 parent d15ca20 commit 73f7da6

File tree

5 files changed

+72
-19
lines changed

5 files changed

+72
-19
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ PRs are welcome.
4747

4848
### TODO
4949

50-
- flesh out this list
50+
- custom types with only one variant should not build a container class
5151
- custom types with no fields are probably not working
5252
- custom types with unlabelled fields are probably not working
5353
- most expressions aren't implemented yet
@@ -62,3 +62,4 @@ PRs are welcome.
6262
- eliminate all todos in source code
6363
- panic should probably raise a custom/stdlib exception
6464
- generate **main** if a module has a main function
65+
- flesh out this list

src/generator.gleam

+27-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ fn generate_binop(
5050
|> string_builder.append_builder(generate_expression(right))
5151
}
5252

53+
fn generate_record_update_fields(
54+
field: python.Field(python.Expression),
55+
) -> StringBuilder {
56+
case field {
57+
python.UnlabelledField(_) ->
58+
panic("Unlabeled fields are not expected on record updates")
59+
python.LabelledField(label, expression) ->
60+
string_builder.new()
61+
|> string_builder.append(label)
62+
|> string_builder.append("=")
63+
|> string_builder.append_builder(generate_expression(expression))
64+
}
65+
}
66+
5367
fn generate_expression(expression: python.Expression) {
5468
case expression {
5569
python.String(string) -> string_builder.from_strings(["\"", string, "\""])
@@ -84,9 +98,20 @@ fn generate_expression(expression: python.Expression) {
8498
generate_expression(expression)
8599
|> string_builder.append(".")
86100
|> string_builder.append(label)
87-
python.Call(function_name, arguments) ->
101+
python.RecordUpdate(record, fields) ->
102+
string_builder.new()
103+
|> string_builder.append("dataclasses.replace(")
104+
|> string_builder.append_builder(generate_expression(record))
105+
|> string_builder.append(", ")
106+
|> string_builder.append_builder(generate_plural(
107+
fields,
108+
generate_record_update_fields,
109+
", ",
110+
))
111+
|> string_builder.append(")")
112+
python.Call(function, arguments) ->
88113
string_builder.new()
89-
|> string_builder.append(function_name)
114+
|> string_builder.append_builder(generate_expression(function))
90115
|> string_builder.append("(")
91116
|> string_builder.append_builder(
92117
arguments

src/python.gleam

+2-7
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,8 @@ pub type Expression {
4343
Todo(Expression)
4444
TupleIndex(tuple: Expression, index: Int)
4545
FieldAccess(container: Expression, label: String)
46-
Call(function_name: String, arguments: List(Expression))
47-
RecordUpdate(
48-
module: option.Option(String),
49-
constructior: String,
50-
record: Expression,
51-
fields: List(#(String, Expression)),
52-
)
46+
Call(function: Expression, arguments: List(Expression))
47+
RecordUpdate(record: Expression, fields: List(Field(Expression)))
5348
BinaryOperator(name: BinaryOperator, left: Expression, right: Expression)
5449
}
5550

src/transformer.gleam

+12-9
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,12 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
105105
glance.NegateBool(expression) ->
106106
python.Not(transform_expression(expression))
107107

108-
glance.Call(glance.Variable(function_name), arguments) -> {
108+
glance.Call(function, arguments) -> {
109109
python.Call(
110-
function_name,
110+
function: transform_expression(function),
111111
arguments: list.map(arguments, transform_call_argument),
112112
)
113113
}
114-
glance.Call(..) -> {
115-
panic as "I don't understand how to handle non-valiable calls"
116-
}
117114

118115
glance.Tuple(expressions) ->
119116
expressions
@@ -128,7 +125,7 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
128125

129126
glance.BinaryOperator(glance.Pipe, left, glance.Variable(function)) -> {
130127
// simple pipe left |> foo
131-
python.Call(function, [transform_expression(left)])
128+
python.Call(python.Variable(function), [transform_expression(left)])
132129
}
133130

134131
glance.BinaryOperator(
@@ -148,7 +145,7 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
148145
arguments_after,
149146
])
150147
|> list.map(transform_call_argument)
151-
python.Call(function, argument_expressions)
148+
python.Call(python.Variable(function), argument_expressions)
152149
}
153150
glance.BinaryOperator(glance.Pipe, _, right) -> {
154151
pprint.debug(right)
@@ -157,8 +154,14 @@ fn transform_expression(expression: glance.Expression) -> python.Expression {
157154
glance.BinaryOperator(name, left, right) ->
158155
transform_binop(name, left, right)
159156

160-
glance.RecordUpdate(module, constructor, record, fields) as expr -> {
161-
python.RecordUpdate(module, constructor, record, fields)
157+
glance.RecordUpdate(record:, fields:, ..) -> {
158+
python.RecordUpdate(
159+
record: transform_expression(record),
160+
fields: fields
161+
|> list.map(fn(tuple) {
162+
python.LabelledField(tuple.0, transform_expression(tuple.1))
163+
}),
164+
)
162165
}
163166

164167
glance.BitString(_) as expr

test/expression_test.gleam

+29
Original file line numberDiff line numberDiff line change
@@ -511,3 +511,32 @@ def main():
511511
",
512512
)
513513
}
514+
515+
pub fn record_update_test() {
516+
"pub type Foo {
517+
Bar(a: Int, b: String)
518+
}
519+
520+
pub fn main() {
521+
let foo = Bar(1, \"who\")
522+
let bar = Bar(..foo, b: \"you\")
523+
}"
524+
|> macabre.compile
525+
|> should.be_ok
526+
|> should.equal(
527+
"from gleam_builtins import *
528+
529+
class Foo:
530+
@dataclasses.dataclass(frozen=True)
531+
class Bar:
532+
a: int
533+
b: str
534+
535+
Bar = Foo.Bar
536+
537+
538+
def main():
539+
foo = Bar(1, \"who\")
540+
bar = dataclasses.replace(foo, b=\"you\")",
541+
)
542+
}

0 commit comments

Comments
 (0)