From 058727fdc76e5ec8efb861d643a0eb9d88da476d Mon Sep 17 00:00:00 2001 From: Dusty Phillips Date: Tue, 20 Aug 2024 13:49:11 -0300 Subject: [PATCH] Start adding a prelude move dataclasses import to it --- README.md | 1 + gleam.toml | 1 + manifest.toml | 1 + src/generator.gleam | 4 +- src/macabre.gleam | 21 +++++- src/python.gleam | 4 ++ src/python_prelude.gleam | 5 ++ src/transformer.gleam | 9 --- test/assignment_test.gleam | 8 ++- test/custom_types_test.gleam | 12 ++-- test/expression_test.gleam | 136 ++++++++++++++++++++++++++--------- test/function_test.gleam | 30 ++++++-- 12 files changed, 171 insertions(+), 61 deletions(-) create mode 100644 src/python_prelude.gleam diff --git a/README.md b/README.md index 2f9270b..fd557b5 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,4 @@ PRs are welcome. - only compiles one module at a time - eliminate all todos in source code - panic should probably raise a custom/stdlib exception +- generate **main** if a module has a main function diff --git a/gleam.toml b/gleam.toml index 4c4c1bf..538269d 100644 --- a/gleam.toml +++ b/gleam.toml @@ -17,6 +17,7 @@ gleam_stdlib = ">= 0.34.0 and < 2.0.0" glance = ">= 0.11.0 and < 1.0.0" argv = ">= 1.0.2 and < 2.0.0" simplifile = ">= 2.0.1 and < 3.0.0" +filepath = ">= 1.0.0 and < 2.0.0" [dev-dependencies] gleescript = ">= 1.4.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 4bf32b3..f96d3e5 100644 --- a/manifest.toml +++ b/manifest.toml @@ -19,6 +19,7 @@ packages = [ [requirements] argv = { version = ">= 1.0.2 and < 2.0.0" } +filepath = { version = ">= 1.0.0 and < 2.0.0" } glance = { version = ">= 0.11.0 and < 1.0.0" } gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleescript = { version = ">= 1.4.0 and < 2.0.0" } diff --git a/src/generator.gleam b/src/generator.gleam index 6cd7149..6aa4281 100644 --- a/src/generator.gleam +++ b/src/generator.gleam @@ -5,6 +5,7 @@ import gleam/option import gleam/string_builder.{type StringBuilder} import pprint import python +import python_prelude pub type Foo @@ -172,7 +173,7 @@ fn generate_type_fields(field: python.Field(python.Type)) -> StringBuilder { fn generate_type_variant(variant: python.Variant) -> StringBuilder { string_builder.new() - |> string_builder.append("@dataclass(frozen=True)\n") + |> string_builder.append("@dataclasses.dataclass(frozen=True)\n") |> string_builder.append("class ") |> string_builder.append(variant.name) |> string_builder.append(":\n") @@ -233,6 +234,7 @@ fn generate_plural( pub fn generate(module: python.Module) -> Result(String, String) { string_builder.new() + |> string_builder.append(python_prelude.prelude) |> string_builder.append_builder(generate_imports(module.imports)) |> string_builder.append_builder(generate_plural( module.custom_types, diff --git a/src/macabre.gleam b/src/macabre.gleam index 4e6a08b..832ce10 100644 --- a/src/macabre.gleam +++ b/src/macabre.gleam @@ -1,10 +1,12 @@ import argv +import filepath import generator import glance import gleam/io import gleam/result import gleam/string import pprint +import python_prelude import simplifile import transformer @@ -27,7 +29,11 @@ pub fn write_output(contents: String, filename: String) -> Result(Nil, String) { } pub fn replace_extension(filename: String) -> String { - filename |> string.drop_right(5) <> "py" + filename |> filepath.strip_extension <> ".py" +} + +pub fn replace_file(path: String, filename: String) -> String { + path |> filepath.directory_name |> filepath.join(filename) } pub fn output_result(filename: String, result: Result(Nil, String)) -> Nil { @@ -44,6 +50,12 @@ pub fn output_result(filename: String, result: Result(Nil, String)) -> Nil { } } +pub fn output_prelude_file(filepath: String) -> Result(Nil, String) { + filepath + |> simplifile.write(python_prelude.gleam_builtins) + |> result.replace_error("Unable to write prelude") +} + pub fn compile(module_contents: String) -> Result(String, String) { module_contents |> parse @@ -57,6 +69,13 @@ pub fn compile_module(filename: String) -> Result(Nil, String) { |> result.try(compile) |> pprint.debug |> result.try(write_output(_, replace_extension(filename))) + |> result.try(fn(_) { + // TODO: eventually, this has to be output to a base directory, + // not one copy per module. + filename + |> replace_file("gleam_builtins.py") + |> output_prelude_file + }) } pub fn main() { diff --git a/src/python.gleam b/src/python.gleam index ed05a7c..4aec742 100644 --- a/src/python.gleam +++ b/src/python.gleam @@ -1,5 +1,9 @@ import gleam/option +pub type Context(a) { + Context(imports: List(Import), item: a) +} + pub type Import { UnqualifiedImport(module: String, name: String, alias: option.Option(String)) } diff --git a/src/python_prelude.gleam b/src/python_prelude.gleam new file mode 100644 index 0000000..d998636 --- /dev/null +++ b/src/python_prelude.gleam @@ -0,0 +1,5 @@ +pub const gleam_builtins = " +import dataclasses +" + +pub const prelude = "from gleam_builtins import *\n\n" diff --git a/src/transformer.gleam b/src/transformer.gleam index af992e9..a1ba82e 100644 --- a/src/transformer.gleam +++ b/src/transformer.gleam @@ -251,17 +251,8 @@ fn transform_custom_type_in_module( module: python.Module, custom_type: glance.Definition(glance.CustomType), ) -> python.Module { - let has_dataclass_import = - list.find(module.imports, fn(imp) { imp == python.dataclass_import }) - - let imports = case has_dataclass_import { - Ok(_) -> module.imports - Error(_) -> [python.dataclass_import, ..module.imports] - } - python.Module( ..module, - imports: imports, custom_types: [ transform_custom_type(custom_type.definition), ..module.custom_types diff --git a/test/assignment_test.gleam b/test/assignment_test.gleam index f739ecc..39f7583 100644 --- a/test/assignment_test.gleam +++ b/test/assignment_test.gleam @@ -9,7 +9,9 @@ pub fn simple_assignment_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): a = \"hello world\"", ) } @@ -23,7 +25,9 @@ pub fn mulitple_simple_assignment_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): a = \"hello world\" b = 42", ) diff --git a/test/custom_types_test.gleam b/test/custom_types_test.gleam index 2778cc0..d951a53 100644 --- a/test/custom_types_test.gleam +++ b/test/custom_types_test.gleam @@ -8,11 +8,10 @@ pub fn single_variant_custom_type_test() { |> macabre.compile |> should.be_ok |> should.equal( - "from dataclasses import dataclass - + "from gleam_builtins import * class Foo: - @dataclass(frozen=True) + @dataclasses.dataclass(frozen=True) class Bar: a: int @@ -31,15 +30,14 @@ pub fn multi_variant_custom_type_test() { |> macabre.compile |> should.be_ok |> should.equal( - "from dataclasses import dataclass - + "from gleam_builtins import * class Foo: - @dataclass(frozen=True) + @dataclasses.dataclass(frozen=True) class Bar: a: int - @dataclass(frozen=True) + @dataclasses.dataclass(frozen=True) class Baz: a: str diff --git a/test/expression_test.gleam b/test/expression_test.gleam index 552050e..14a3370 100644 --- a/test/expression_test.gleam +++ b/test/expression_test.gleam @@ -9,7 +9,9 @@ pub fn string_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): \"bar\" ", ) @@ -22,7 +24,9 @@ pub fn int_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 42 ", ) @@ -35,7 +39,9 @@ pub fn float_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 12.5 ", ) @@ -48,7 +54,9 @@ pub fn tuple_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): (42, 12.5, \"foo\") ", ) @@ -61,7 +69,9 @@ pub fn true_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): True ", ) @@ -74,7 +84,9 @@ pub fn false_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): False ", ) @@ -87,7 +99,9 @@ pub fn variable_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): println(a) ", ) @@ -101,7 +115,9 @@ pub fn negate_int_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): a = -1 b = -a", ) @@ -114,7 +130,9 @@ pub fn negate_bool_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): b = not True", ) } @@ -126,7 +144,9 @@ pub fn empty_panic_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): raise BaseException(\"panic expression evaluated\") ", ) @@ -139,7 +159,9 @@ pub fn string_panic_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): raise BaseException(\"my custom panic\") ", ) @@ -152,7 +174,9 @@ pub fn empty_todo_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): raise NotImplementedError(\"This has not yet been implemented\") ", ) @@ -165,7 +189,9 @@ pub fn string_todo_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): raise NotImplementedError(\"much is yet to be done\") ", ) @@ -178,7 +204,9 @@ pub fn tuple_index_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): (42, 12.5, \"foo\")[1] ", ) @@ -191,7 +219,9 @@ pub fn field_access_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): foo.b ", ) @@ -204,7 +234,9 @@ pub fn binop_int_add_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40 + 2 ", ) @@ -217,7 +249,9 @@ pub fn binop_float_add_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40.2 + 2.5 ", ) @@ -230,7 +264,9 @@ pub fn binop_concat_add_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): \"hello \" + \"world\" ", ) @@ -243,7 +279,9 @@ pub fn binop_int_sub_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40 - 2 ", ) @@ -256,7 +294,9 @@ pub fn binop_float_sub_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40.2 - 2.5 ", ) @@ -269,7 +309,9 @@ pub fn binop_int_div_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40 // 2 ", ) @@ -282,7 +324,9 @@ pub fn binop_float_div_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 40.2 / 2.5 ", ) @@ -295,7 +339,9 @@ pub fn binop_int_modulo_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5 % 2 ", ) @@ -308,7 +354,9 @@ pub fn equality_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5 == 5 ", ) @@ -321,7 +369,9 @@ pub fn inequality_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5 != 2 ", ) @@ -334,7 +384,9 @@ pub fn lt_int_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5 < 2 ", ) @@ -347,7 +399,9 @@ pub fn lt_float_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5.0 < 2.0 ", ) @@ -360,7 +414,9 @@ pub fn lt_eq_int_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5 <= 2 ", ) @@ -373,7 +429,9 @@ pub fn lt_eq_float_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): 5.0 <= 2.0 ", ) @@ -386,7 +444,9 @@ pub fn logical_or_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): True or False ", ) @@ -399,7 +459,9 @@ pub fn logical_and_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): True and False ", ) @@ -412,7 +474,9 @@ pub fn simple_pipe_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): println(\"foo\") ", ) @@ -425,7 +489,9 @@ pub fn capture_pipe_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): println(\"a\", \"foo\", \"b\") ", ) @@ -438,7 +504,9 @@ pub fn call_expression_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def main(): + "from gleam_builtins import * + +def main(): foo(\"bar\") ", ) diff --git a/test/function_test.gleam b/test/function_test.gleam index 36ac03f..b9aee79 100644 --- a/test/function_test.gleam +++ b/test/function_test.gleam @@ -6,7 +6,11 @@ pub fn external_python_test() { fn println() -> nil" |> macabre.compile |> should.be_ok - |> should.equal("from mylib import println\n\n\n") + |> should.equal( + "from gleam_builtins import * + +from mylib import println\n\n\n", + ) } pub fn skip_external_javascript_test() { @@ -19,7 +23,9 @@ fn println() -> nil" |> macabre.compile |> should.be_ok |> should.equal( - "def println(): + "from gleam_builtins import * + +def println(): pass", ) } @@ -34,7 +40,9 @@ fn println() -> nil" |> macabre.compile |> should.be_ok |> should.equal( - "def println(): + "from gleam_builtins import * + +def println(): pass", ) } @@ -44,7 +52,9 @@ pub fn empty_body_no_external_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def println(): + "from gleam_builtins import * + +def println(): pass", ) } @@ -54,7 +64,9 @@ pub fn function_with_string_param_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def println(arg): + "from gleam_builtins import * + +def println(arg): pass", ) } @@ -64,7 +76,9 @@ pub fn function_with_two_string_params_test() { |> macabre.compile |> should.be_ok |> should.equal( - "def println(arg, other): + "from gleam_builtins import * + +def println(arg, other): pass", ) } @@ -77,7 +91,9 @@ fn func2() -> nil {} |> macabre.compile |> should.be_ok |> should.equal( - "def func1(): + "from gleam_builtins import * + +def func1(): pass