diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..34cd044 --- /dev/null +++ b/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 - present Dusty Phillips + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/gleam.toml b/gleam.toml index d179003..4b7f11c 100644 --- a/gleam.toml +++ b/gleam.toml @@ -21,6 +21,7 @@ filepath = ">= 1.0.0 and < 2.0.0" glexer = ">= 1.0.1 and < 2.0.0" tom = ">= 1.0.1 and < 2.0.0" shellout = ">= 1.6.0 and < 2.0.0" +glimpse = ">= 0.0.2 and < 1.0.0" [dev-dependencies] gleescript = ">= 1.4.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 3300d7a..cd78a10 100644 --- a/manifest.toml +++ b/manifest.toml @@ -9,14 +9,15 @@ packages = [ { name = "glam", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "66EC3BCD632E51EED029678F8DF419659C1E57B1A93D874C5131FE220DFAD2B2" }, { name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" }, { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, - { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, + { name = "gleam_erlang", version = "0.26.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "3DF72F95F4716883FA51396FB0C550ED3D55195B541568CAF09745984FD37AD1" }, + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, { name = "gleescript", version = "1.4.0", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_erlang", "gleam_stdlib", "simplifile", "snag", "tom"], otp_app = "gleescript", source = "hex", outer_checksum = "8CDDD29F91064E69950A91A40061785F10275ADB70A0520075591F61A724C455" }, { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, { name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" }, + { name = "glimpse", version = "0.0.2", build_tools = ["gleam"], requirements = ["glance", "gleam_stdlib"], otp_app = "glimpse", source = "hex", outer_checksum = "66CB2E9BB092C865451468847B1F377F2F1E49B71659D6627B3052C02E4BC1E8" }, { name = "pprint", version = "1.0.3", build_tools = ["gleam"], requirements = ["glam", "gleam_stdlib"], otp_app = "pprint", source = "hex", outer_checksum = "76BBB92E23D12D954BD452686543F29EDE8EBEBB7FC0ACCBCA66EEF276EC3A06" }, { name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" }, - { name = "simplifile", version = "2.0.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "5FFEBD0CAB39BDD343C3E1CCA6438B2848847DC170BA2386DF9D7064F34DF000" }, + { name = "simplifile", version = "2.1.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "BDD04F5D31D6D34E2EDFAEF0B68A6297AEC939888C3BFCE61133DE13857F6DA2" }, { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, { name = "temporary", version = "1.0.0", build_tools = ["gleam"], requirements = ["envoy", "exception", "filepath", "gleam_crypto", "gleam_stdlib", "simplifile"], otp_app = "temporary", source = "hex", outer_checksum = "51C0FEF4D72CE7CA507BD188B21C1F00695B3D5B09D7DFE38240BFD3A8E1E9B3" }, { name = "tom", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "9EECB60150E834A07238BD5C7DF1FF07F7D4C5862BB8A773923D1981C7875FB0" }, @@ -30,6 +31,7 @@ gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } gleescript = { version = ">= 1.4.0 and < 2.0.0" } gleeunit = { version = ">= 1.2.0 and < 2.0.0" } glexer = { version = ">= 1.0.1 and < 2.0.0" } +glimpse = { version = ">= 0.0.2 and < 1.0.0" } pprint = { version = ">= 1.0.3 and < 2.0.0" } shellout = { version = ">= 1.6.0 and < 2.0.0" } simplifile = { version = ">= 2.0.1 and < 3.0.0" } diff --git a/src/compiler.gleam b/src/compiler.gleam index 41786a3..52c6e52 100644 --- a/src/compiler.gleam +++ b/src/compiler.gleam @@ -15,11 +15,11 @@ pub fn compile_module(glance_module: glance.Module) -> String { pub fn compile_package(package: package.GleamPackage) -> package.CompiledPackage { package.CompiledPackage( project: package.project, - has_main: dict.get(package.modules, package.project.name <> ".gleam") - |> result.try(fn(mod) { mod.functions |> has_main_function }) + has_main: dict.get(package.package.modules, package.project.name) + |> result.try(fn(mod) { mod.module.functions |> has_main_function }) |> result.is_ok, - modules: package.modules - |> dict.map_values(fn(_key, value) { compile_module(value) }), + modules: package.package.modules + |> dict.map_values(fn(_key, value) { compile_module(value.module) }), external_import_files: package.external_import_files, ) } diff --git a/src/compiler/package.gleam b/src/compiler/package.gleam index 7235fa8..e15a4d1 100644 --- a/src/compiler/package.gleam +++ b/src/compiler/package.gleam @@ -15,11 +15,12 @@ import gleam/list import gleam/result import gleam/set import gleam/string +import glimpse pub type GleamPackage { GleamPackage( project: project.Project, - modules: dict.Dict(String, glance.Module), + package: glimpse.Package, external_import_files: set.Set(String), ) } @@ -39,72 +40,35 @@ pub fn load( gleam_project: project.Project, ) -> Result(GleamPackage, errors.Error) { use _ <- result.try(filesystem.is_directory(project.src_dir(gleam_project))) - use package <- result.try(load_module( - GleamPackage(gleam_project, dict.new(), external_import_files: set.new()), - project.entry_point(gleam_project), + use glimpse_package <- result.try(load_glimpse_package(gleam_project)) + Ok(GleamPackage( + gleam_project, + glimpse_package, + python_externals(glimpse_package), )) - Ok(package) } -/// Parse the module and add it to the package's modules, if it can be parsed. -/// Then recursively parse any modules it imports. -fn load_module( - package: GleamPackage, - module_path: String, -) -> Result(GleamPackage, errors.Error) { - case dict.get(package.modules, module_path) { - Ok(_) -> Ok(package) - Error(_) -> { - let module_result = - project.build_src_dir(package.project) - |> filepath.join(module_path) - |> filesystem.read - |> result.try(parse(_, module_path)) - - case module_result { - Error(err) -> Error(err) - Ok(module_contents) -> { - add_module(package, module_path, module_contents) - |> Ok - |> list.fold(module_contents.imports, _, fold_load_module) - } - } +fn load_glimpse_package( + project: project.Project, +) -> Result(glimpse.Package, errors.Error) { + glimpse.load_package(project.name, fn(module_name) { + let path = + filepath.join(project.build_src_dir(project), module_name <> ".gleam") + filesystem.read(path) + }) + |> result.map_error(fn(error) { + case error { + glimpse.LoadError(error) -> error + glimpse.ParseError(glance_error, name, content) -> + errors.GlanceParseError(glance_error, name, content) } - } -} - -fn fold_load_module( - package_result: Result(GleamPackage, errors.Error), - import_def: glance.Definition(glance.Import), -) -> Result(GleamPackage, errors.Error) { - case package_result { - Error(error) -> Error(error) - Ok(package) -> - load_module(package, import_def.definition.module <> ".gleam") - } -} - -fn parse( - contents: String, - filename: String, -) -> Result(glance.Module, errors.Error) { - glance.module(contents) - |> result.map_error(errors.GlanceParseError(_, filename, contents)) + }) } -fn add_module( - package: GleamPackage, - module_path: String, - module_contents: glance.Module, -) -> GleamPackage { - GleamPackage( - ..package, - modules: dict.insert(package.modules, module_path, module_contents), - external_import_files: set.union( - python_external_modules(module_contents.functions), - package.external_import_files, - ), - ) +fn python_externals(package: glimpse.Package) -> set.Set(String) { + dict.fold(package.modules, set.new(), fn(externals, _key, module) { + set.union(externals, python_external_modules(module.module.functions)) + }) } fn python_external_modules( diff --git a/src/compiler/project.gleam b/src/compiler/project.gleam index 3c9b6e0..f6467b5 100644 --- a/src/compiler/project.gleam +++ b/src/compiler/project.gleam @@ -60,6 +60,8 @@ pub fn build_dir(project: Project) -> String { |> filepath.join("build") } +/// The directory that all package sources (including dependencies) +/// are copied into and loaded from. pub fn build_src_dir(project: Project) -> String { project |> build_dir diff --git a/test/compiler/directory_structure_tests.gleam b/test/compiler/directory_structure_tests.gleam index 0e1323e..470d3e1 100644 --- a/test/compiler/directory_structure_tests.gleam +++ b/test/compiler/directory_structure_tests.gleam @@ -124,7 +124,7 @@ pub fn package_compile_test_with_nested_folders_test() { // load should.equal(gleam_project.base_directory, project_files.base_dir) - gleam_package.modules + gleam_package.package.modules |> dict.size |> should.equal(2) gleam_package.external_import_files |> set.size |> should.equal(2)