From 965058def2383321871aa7fb584ad0b2162ed309 Mon Sep 17 00:00:00 2001 From: DOOM1EM8Cover Date: Mon, 28 Aug 2023 20:09:31 +0100 Subject: [PATCH] More work on a lot of subjects --- .gitignore | 2 - .idea/comline-rs.iml | 13 +- .idea/inspectionProfiles/Project_Default.xml | 21 + .idea/vcs.xml | 6 +- Cargo.toml | 31 +- LICENSE | 21 - README.md | 11 +- .gitattributes => core/.gitattributes | 0 core/.gitignore | 18 + core/.idea/.gitignore | 8 + core/.idea/comline-rs.iml | 13 + core/.idea/modules.xml | 8 + core/.idea/vcs.xml | 6 + core/Cargo.toml | 32 ++ core/README.md | 20 + core/examples/codegen/simple_gen.rs | 32 ++ {examples => core/examples}/dev.ids | 2 - {examples => core/examples}/example.ids | 9 - {examples => core/examples}/greet.ids | 0 {examples => core/examples}/imports/ant.cls | 0 {examples => core/examples}/imports/food.cls | 0 {examples => core/examples}/imports/hole.cls | 0 {examples => core/examples}/simple.ids | 18 +- .../enum_variant_associated_kind.ids | 7 + .../protocol_function_blanket_impl.ids | 8 + .../protocol_property_discoverable.ids | 5 + .../protocol_provider_and_provenance.ids | 13 + core/src/autodoc/document.rs | 44 ++ core/src/autodoc/mod.rs | 50 ++ core/src/autodoc/package.rs | 6 + core/src/autodoc/schema.rs | 70 +++ core/src/codegen/mod.rs | 28 + core/src/codegen/rust.rs | 102 ++++ core/src/langlib/lang_items.rs | 17 + core/src/langlib/mod.rs | 76 +++ {src => core/src}/lib.rs | 9 +- core/src/project/build/mod.rs | 170 +++++++ core/src/project/idl/ast.rs | 49 ++ core/src/project/idl/constants.rs | 8 + core/src/project/idl/idc.pest | 130 +++++ core/src/project/idl/mod.rs | 5 + core/src/project/idl/parser.rs | 47 ++ core/src/project/idl/parser_new.rs | 179 +++++++ core/src/project/ir/compiler/interpret.rs | 27 + core/src/project/ir/compiler/mod.rs | 25 + core/src/project/ir/compiler/report.rs | 39 ++ core/src/project/ir/context.rs | 117 +++++ core/src/project/ir/diff/mod.rs | 117 +++++ core/src/project/ir/diff/report.rs | 30 ++ core/src/project/ir/diff/versioning.rs | 60 +++ core/src/project/ir/frozen/loader.rs | 66 +++ core/src/project/ir/frozen/meta.rs | 67 +++ core/src/project/ir/frozen/mod.rs | 61 +++ core/src/project/ir/interpreter/freezing.rs | 212 ++++++++ core/src/project/ir/interpreter/interpret.rs | 32 ++ core/src/project/ir/interpreter/mod.rs | 57 +++ core/src/project/ir/interpreter/report.rs | 50 ++ core/src/project/ir/mod.rs | 14 + core/src/project/mod.rs | 41 ++ core/src/report.rs | 54 ++ core/src/schema/idl/ast/mod.rs | 3 + .../src/schema/idl/ast/old_unit.rs | 34 +- core/src/schema/idl/ast/unit.rs | 167 ++++++ core/src/schema/idl/constants.rs | 9 + core/src/schema/idl/idl.pest | 261 ++++++++++ core/src/schema/idl/mod.rs | 5 + core/src/schema/idl/parser.rs | 389 ++++++++++++++ core/src/schema/idl/parser_new.rs | 478 ++++++++++++++++++ .../ir/compiler/interpreted/interpreter.rs | 301 +++++++++++ .../src/schema/ir/compiler/interpreted/mod.rs | 6 + .../ir/compiler/interpreted/primitive.rs | 186 +++++++ .../ir/compiler/interpreted/sanitizer.rs | 12 + .../interpreted/serialization/messagepack.rs | 20 + .../compiler/interpreted/serialization/mod.rs | 0 .../ir/compiler/interpreter/incremental.rs | 173 +++++++ .../ir/compiler/interpreter/meta_stage.rs | 100 ++++ .../src/schema/ir/compiler/interpreter/mod.rs | 4 + .../ir/compiler/interpreter/object_stage.rs | 17 + .../ir/compiler/interpreter/semi_frozen.rs | 14 + core/src/schema/ir/compiler/mod.rs | 33 ++ core/src/schema/ir/compiler/report.rs | 47 ++ core/src/schema/ir/context.rs | 73 +++ core/src/schema/ir/diff/mod.rs | 118 +++++ core/src/schema/ir/frozen/blob.rs | 62 +++ core/src/schema/ir/frozen/commit.rs | 18 + core/src/schema/ir/frozen/mod.rs | 5 + core/src/schema/ir/frozen/tree.rs | 20 + core/src/schema/ir/frozen/unit.rs | 87 ++++ core/src/schema/ir/mod.rs | 5 + core/src/schema/mod.rs | 3 + core/src/utils/codemap.rs | 309 +++++++++++ core/src/utils/mod.rs | 2 + core/src/utils/templating.rs | 32 ++ core/tests/autodoc/mod.rs | 46 ++ core/tests/idl/ir/mod.rs | 44 ++ core/tests/idl/mod.rs | 4 + {tests => core/tests}/idl/simple.ids | 23 +- core/tests/idl/stdlib/mod.rs | 2 + core/tests/idl/stdlib/validators.rs | 101 ++++ core/tests/idl/unit.rs | 249 +++++++++ core/tests/mod.rs | 3 + core/tests/package/build.rs | 15 + core/tests/package/compile.rs | 18 + core/tests/package/mod.rs | 43 ++ core/tests/package/parse.rs | 63 +++ core/tests/package/test/.frozen/index | 1 + ...d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 | 1 + core/tests/package/test/config.idp | 22 + .../generated/0.0.1/rust/1.70.0/health.rs | 0 core/tests/package/test/health.ids | 13 + core/tests/package/test/ping.ids | 15 + core_stdlib/Cargo.toml | 8 + core_stdlib/src/lib.rs | 0 core_stdlib/src/package/config.idp | 3 + .../src/package/validators/string_bounds.ids | 29 ++ docs/.gitignore | 2 + docs/README.md | 3 + docs/docs/ir/cas.md | 16 + docs/docs/ir/index.md | 1 - .../code-generation/compiled-languages.md | 19 + .../code-generation/dynamic-languages.md | 35 ++ docs/docs/planning/code-generation/index.md | 17 + docs/docs/planning/coge-generaiton.md | 35 -- docs/docs/planning/ir-generation.md | 2 +- docs/docs/planning/syntax.md | 2 - docs/docs/planning/syntax/proposal #1.md | 10 - docs/docs/proposals/auto_documentation.md | 8 + docs/docs/proposals/index.md | 4 + docs/shell.nix | 0 examples/{ => codegen}/simple_gen.rs | 0 package-manager-server/Cargo.toml | 16 + package-manager-server/README.md | 11 + package-manager-server/src/main.rs | 19 + package-manager-server/src/rest/mod.rs | 43 ++ package-manager-server/src/rest/package.rs | 43 ++ package-manager-server/src/rest/publish.rs | 23 + package-manager-server/src/rest/user.rs | 29 ++ package-manager-server/src/storage/mod.rs | 33 ++ .../src/storage/projects.rs | 20 + package-manager-server/src/storage/users.rs | 6 + package-manager-server/src/web_ui/mod.rs | 29 ++ package-manager-server/src/web_ui/package.rs | 28 + package-manager-server/src/web_ui/template.rs | 24 + package-manager-server/templates/index.html | 25 + package-manager-server/templates/package.html | 50 ++ .../templates/packages.html | 39 ++ package-manager/Cargo.toml | 14 + package-manager/README.md | 33 ++ package-manager/src/client/mod.rs | 9 + package-manager/src/client/publish.rs | 78 +++ package-manager/src/main.rs | 65 +++ package-manager/src/manager/dependencies.rs | 26 + package-manager/src/manager/indexer.rs | 6 + package-manager/src/manager/mod.rs | 11 + package-manager/src/tests/client/mod.rs | 43 ++ package-manager/src/tests/mod.rs | 1 + proposals/enum_variant_associated_kind.ids | 0 proposals/protocol_function_blanket_impl.ids | 0 proposals/protocol_property_discoverable.ids | 6 + .../protocol_provider_and_provenance.ids | 9 + runtime/Cargo.toml | 11 + runtime/src/casy/mod.rs | 17 + runtime/src/casy/open_rpc.rs | 6 + runtime/src/handler.rs | 10 + runtime/src/lib.rs | 5 + runtime/src/prelude.rs | 6 + runtime/src/wire_format/mod.rs | 2 + runtime/src/wire_format/msgpack.rs | 6 + runtime/tests/casy/mod.rs | 0 runtime/tests/mod.rs | 1 + src/codegen.rs | 1 - src/codegen/mod.rs | 0 src/codegen/rust.rs | 77 --- src/idl/parser.rs | 219 -------- src/idl/pest.rs | 275 ---------- src/ir.rs | 8 - src/ir/compiler.rs | 43 -- src/ir/context.rs | 36 -- src/ir/new_unit.rs | 121 ----- src/ir/primitive.rs | 94 ---- src/project/idl/ast.rs | 0 src/project/idl/idc.pest | 7 + src/project/idl/mod.rs | 0 src/project/idl/parser.rs | 16 + src/project/ir/mod.rs | 0 src/project/mod.rs | 0 src/schema/idl/ast/mod.rs | 3 + src/schema/idl/ast/old_unit.rs | 116 +++++ src/schema/idl/ast/unit.rs | 160 ++++++ src/{ => schema}/idl/constants.rs | 2 + src/{ => schema}/idl/idl.pest | 100 ++-- src/{idl.rs => schema/idl/mod.rs} | 2 +- src/schema/idl/parser.rs | 370 ++++++++++++++ .../ir/compiler/interpreted/frozen/blob.rs | 0 .../ir/compiler/interpreted/frozen/commit.rs | 0 .../ir/compiler/interpreted/frozen/mod.rs | 0 .../ir/compiler/interpreted/frozen/tree.rs | 0 .../ir/compiler/interpreted/frozen/unit.rs | 0 .../ir/compiler/interpreted/frozen_unit.rs | 83 +++ .../ir/compiler/interpreted/interpreter.rs | 168 ++++++ src/schema/ir/compiler/interpreted/mod.rs | 10 + .../ir/compiler/interpreted/primitive.rs | 71 +++ src/schema/ir/compiler/interpreted/report.rs | 43 ++ .../ir/compiler/interpreted}/sanitizer.rs | 0 .../interpreted}/serialization/messagepack.rs | 6 +- .../interpreted/serialization}/mod.rs | 9 +- .../ir/compiler/interpreter/incremental.rs | 173 +++++++ src/schema/ir/compiler/interpreter/mod.rs | 1 + .../ir/compiler/interpreter/relations.rs | 0 src/schema/ir/compiler/mod.rs | 26 + src/schema/ir/context.rs | 51 ++ src/schema/ir/mod.rs | 4 + src/schema/mod.rs | 0 src/stdlib/lang_items.rs | 14 - src/stdlib/validators/string_bounds.ids | 12 - tests/idl/congregation.idc | 0 tests/idl/mod.rs | 1 - tests/idl/unit.rs | 72 --- tests/{ => idl}/units/dev.ids | 0 tests/idl/units/mod.rs | 32 ++ tests/{ => idl}/units/protocol/args.ids | 0 tests/ir/mod.rs | 0 tests/mod.rs | 2 - tests/stdlib/mod.rs | 0 tests/stdlib/validators.rs | 0 tests/units/consts.ids | 2 - tests/units/mod.rs | 28 - 227 files changed, 8274 insertions(+), 1200 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 LICENSE rename .gitattributes => core/.gitattributes (100%) create mode 100644 core/.gitignore create mode 100644 core/.idea/.gitignore create mode 100644 core/.idea/comline-rs.iml create mode 100644 core/.idea/modules.xml create mode 100644 core/.idea/vcs.xml create mode 100644 core/Cargo.toml create mode 100644 core/README.md create mode 100644 core/examples/codegen/simple_gen.rs rename {examples => core/examples}/dev.ids (78%) rename {examples => core/examples}/example.ids (93%) rename {examples => core/examples}/greet.ids (100%) rename {examples => core/examples}/imports/ant.cls (100%) rename {examples => core/examples}/imports/food.cls (100%) rename {examples => core/examples}/imports/hole.cls (100%) rename {examples => core/examples}/simple.ids (78%) create mode 100644 core/proposals/enum_variant_associated_kind.ids create mode 100644 core/proposals/protocol_function_blanket_impl.ids create mode 100644 core/proposals/protocol_property_discoverable.ids create mode 100644 core/proposals/protocol_provider_and_provenance.ids create mode 100644 core/src/autodoc/document.rs create mode 100644 core/src/autodoc/mod.rs create mode 100644 core/src/autodoc/package.rs create mode 100644 core/src/autodoc/schema.rs create mode 100644 core/src/codegen/mod.rs create mode 100644 core/src/codegen/rust.rs create mode 100644 core/src/langlib/lang_items.rs create mode 100644 core/src/langlib/mod.rs rename {src => core/src}/lib.rs (66%) create mode 100644 core/src/project/build/mod.rs create mode 100644 core/src/project/idl/ast.rs create mode 100644 core/src/project/idl/constants.rs create mode 100644 core/src/project/idl/idc.pest create mode 100644 core/src/project/idl/mod.rs create mode 100644 core/src/project/idl/parser.rs create mode 100644 core/src/project/idl/parser_new.rs create mode 100644 core/src/project/ir/compiler/interpret.rs create mode 100644 core/src/project/ir/compiler/mod.rs create mode 100644 core/src/project/ir/compiler/report.rs create mode 100644 core/src/project/ir/context.rs create mode 100644 core/src/project/ir/diff/mod.rs create mode 100644 core/src/project/ir/diff/report.rs create mode 100644 core/src/project/ir/diff/versioning.rs create mode 100644 core/src/project/ir/frozen/loader.rs create mode 100644 core/src/project/ir/frozen/meta.rs create mode 100644 core/src/project/ir/frozen/mod.rs create mode 100644 core/src/project/ir/interpreter/freezing.rs create mode 100644 core/src/project/ir/interpreter/interpret.rs create mode 100644 core/src/project/ir/interpreter/mod.rs create mode 100644 core/src/project/ir/interpreter/report.rs create mode 100644 core/src/project/ir/mod.rs create mode 100644 core/src/project/mod.rs create mode 100644 core/src/report.rs create mode 100644 core/src/schema/idl/ast/mod.rs rename src/ir/unit.rs => core/src/schema/idl/ast/old_unit.rs (81%) create mode 100644 core/src/schema/idl/ast/unit.rs create mode 100644 core/src/schema/idl/constants.rs create mode 100644 core/src/schema/idl/idl.pest create mode 100644 core/src/schema/idl/mod.rs create mode 100644 core/src/schema/idl/parser.rs create mode 100644 core/src/schema/idl/parser_new.rs create mode 100644 core/src/schema/ir/compiler/interpreted/interpreter.rs create mode 100644 core/src/schema/ir/compiler/interpreted/mod.rs create mode 100644 core/src/schema/ir/compiler/interpreted/primitive.rs create mode 100644 core/src/schema/ir/compiler/interpreted/sanitizer.rs create mode 100644 core/src/schema/ir/compiler/interpreted/serialization/messagepack.rs rename src/ir/serialization.rs => core/src/schema/ir/compiler/interpreted/serialization/mod.rs (100%) create mode 100644 core/src/schema/ir/compiler/interpreter/incremental.rs create mode 100644 core/src/schema/ir/compiler/interpreter/meta_stage.rs create mode 100644 core/src/schema/ir/compiler/interpreter/mod.rs create mode 100644 core/src/schema/ir/compiler/interpreter/object_stage.rs create mode 100644 core/src/schema/ir/compiler/interpreter/semi_frozen.rs create mode 100644 core/src/schema/ir/compiler/mod.rs create mode 100644 core/src/schema/ir/compiler/report.rs create mode 100644 core/src/schema/ir/context.rs create mode 100644 core/src/schema/ir/diff/mod.rs create mode 100644 core/src/schema/ir/frozen/blob.rs create mode 100644 core/src/schema/ir/frozen/commit.rs create mode 100644 core/src/schema/ir/frozen/mod.rs create mode 100644 core/src/schema/ir/frozen/tree.rs create mode 100644 core/src/schema/ir/frozen/unit.rs create mode 100644 core/src/schema/ir/mod.rs create mode 100644 core/src/schema/mod.rs create mode 100644 core/src/utils/codemap.rs create mode 100644 core/src/utils/mod.rs create mode 100644 core/src/utils/templating.rs create mode 100644 core/tests/autodoc/mod.rs create mode 100644 core/tests/idl/ir/mod.rs create mode 100644 core/tests/idl/mod.rs rename {tests => core/tests}/idl/simple.ids (67%) create mode 100644 core/tests/idl/stdlib/mod.rs create mode 100644 core/tests/idl/stdlib/validators.rs create mode 100644 core/tests/idl/unit.rs create mode 100644 core/tests/mod.rs create mode 100644 core/tests/package/build.rs create mode 100644 core/tests/package/compile.rs create mode 100644 core/tests/package/mod.rs create mode 100644 core/tests/package/parse.rs create mode 100644 core/tests/package/test/.frozen/index create mode 100644 core/tests/package/test/.frozen/objects/project/d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 create mode 100644 core/tests/package/test/config.idp rename src/stdlib/validators/mod.rs => core/tests/package/test/generated/0.0.1/rust/1.70.0/health.rs (100%) create mode 100644 core/tests/package/test/health.ids create mode 100644 core/tests/package/test/ping.ids create mode 100644 core_stdlib/Cargo.toml create mode 100644 core_stdlib/src/lib.rs create mode 100644 core_stdlib/src/package/config.idp create mode 100644 core_stdlib/src/package/validators/string_bounds.ids create mode 100644 docs/.gitignore create mode 100644 docs/docs/ir/cas.md create mode 100644 docs/docs/planning/code-generation/compiled-languages.md create mode 100644 docs/docs/planning/code-generation/dynamic-languages.md create mode 100644 docs/docs/planning/code-generation/index.md delete mode 100644 docs/docs/planning/syntax.md delete mode 100644 docs/docs/planning/syntax/proposal #1.md create mode 100644 docs/docs/proposals/auto_documentation.md create mode 100644 docs/docs/proposals/index.md create mode 100644 docs/shell.nix rename examples/{ => codegen}/simple_gen.rs (100%) create mode 100644 package-manager-server/Cargo.toml create mode 100644 package-manager-server/README.md create mode 100644 package-manager-server/src/main.rs create mode 100644 package-manager-server/src/rest/mod.rs create mode 100644 package-manager-server/src/rest/package.rs create mode 100644 package-manager-server/src/rest/publish.rs create mode 100644 package-manager-server/src/rest/user.rs create mode 100644 package-manager-server/src/storage/mod.rs create mode 100644 package-manager-server/src/storage/projects.rs create mode 100644 package-manager-server/src/storage/users.rs create mode 100644 package-manager-server/src/web_ui/mod.rs create mode 100644 package-manager-server/src/web_ui/package.rs create mode 100644 package-manager-server/src/web_ui/template.rs create mode 100644 package-manager-server/templates/index.html create mode 100644 package-manager-server/templates/package.html create mode 100644 package-manager-server/templates/packages.html create mode 100644 package-manager/Cargo.toml create mode 100644 package-manager/README.md create mode 100644 package-manager/src/client/mod.rs create mode 100644 package-manager/src/client/publish.rs create mode 100644 package-manager/src/main.rs create mode 100644 package-manager/src/manager/dependencies.rs create mode 100644 package-manager/src/manager/indexer.rs create mode 100644 package-manager/src/manager/mod.rs create mode 100644 package-manager/src/tests/client/mod.rs create mode 100644 package-manager/src/tests/mod.rs create mode 100644 proposals/enum_variant_associated_kind.ids create mode 100644 proposals/protocol_function_blanket_impl.ids create mode 100644 proposals/protocol_property_discoverable.ids create mode 100644 proposals/protocol_provider_and_provenance.ids create mode 100644 runtime/Cargo.toml create mode 100644 runtime/src/casy/mod.rs create mode 100644 runtime/src/casy/open_rpc.rs create mode 100644 runtime/src/handler.rs create mode 100644 runtime/src/lib.rs create mode 100644 runtime/src/prelude.rs create mode 100644 runtime/src/wire_format/mod.rs create mode 100644 runtime/src/wire_format/msgpack.rs create mode 100644 runtime/tests/casy/mod.rs create mode 100644 runtime/tests/mod.rs delete mode 100644 src/codegen.rs create mode 100644 src/codegen/mod.rs delete mode 100644 src/codegen/rust.rs delete mode 100644 src/idl/parser.rs delete mode 100644 src/idl/pest.rs delete mode 100644 src/ir.rs delete mode 100644 src/ir/compiler.rs delete mode 100644 src/ir/context.rs delete mode 100644 src/ir/new_unit.rs delete mode 100644 src/ir/primitive.rs create mode 100644 src/project/idl/ast.rs create mode 100644 src/project/idl/idc.pest create mode 100644 src/project/idl/mod.rs create mode 100644 src/project/idl/parser.rs create mode 100644 src/project/ir/mod.rs create mode 100644 src/project/mod.rs create mode 100644 src/schema/idl/ast/mod.rs create mode 100644 src/schema/idl/ast/old_unit.rs create mode 100644 src/schema/idl/ast/unit.rs rename src/{ => schema}/idl/constants.rs (73%) rename src/{ => schema}/idl/idl.pest (67%) rename src/{idl.rs => schema/idl/mod.rs} (62%) create mode 100644 src/schema/idl/parser.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen/blob.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen/commit.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen/mod.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen/tree.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen/unit.rs create mode 100644 src/schema/ir/compiler/interpreted/frozen_unit.rs create mode 100644 src/schema/ir/compiler/interpreted/interpreter.rs create mode 100644 src/schema/ir/compiler/interpreted/mod.rs create mode 100644 src/schema/ir/compiler/interpreted/primitive.rs create mode 100644 src/schema/ir/compiler/interpreted/report.rs rename src/{ir => schema/ir/compiler/interpreted}/sanitizer.rs (100%) rename src/{ir => schema/ir/compiler/interpreted}/serialization/messagepack.rs (60%) rename src/{stdlib => schema/ir/compiler/interpreted/serialization}/mod.rs (55%) create mode 100644 src/schema/ir/compiler/interpreter/incremental.rs create mode 100644 src/schema/ir/compiler/interpreter/mod.rs create mode 100644 src/schema/ir/compiler/interpreter/relations.rs create mode 100644 src/schema/ir/compiler/mod.rs create mode 100644 src/schema/ir/context.rs create mode 100644 src/schema/ir/mod.rs create mode 100644 src/schema/mod.rs delete mode 100644 src/stdlib/lang_items.rs delete mode 100644 src/stdlib/validators/string_bounds.ids create mode 100644 tests/idl/congregation.idc delete mode 100644 tests/idl/mod.rs delete mode 100644 tests/idl/unit.rs rename tests/{ => idl}/units/dev.ids (100%) create mode 100644 tests/idl/units/mod.rs rename tests/{ => idl}/units/protocol/args.ids (100%) create mode 100644 tests/ir/mod.rs delete mode 100644 tests/mod.rs create mode 100644 tests/stdlib/mod.rs create mode 100644 tests/stdlib/validators.rs delete mode 100644 tests/units/consts.ids delete mode 100644 tests/units/mod.rs diff --git a/.gitignore b/.gitignore index ad1ba3b..6e5423d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,3 @@ Cargo.lock *.pdb -# Docs build -/docs/.cache/ diff --git a/.idea/comline-rs.iml b/.idea/comline-rs.iml index a6b108e..426e149 100644 --- a/.idea/comline-rs.iml +++ b/.idea/comline-rs.iml @@ -2,9 +2,16 @@ - - - + + + + + + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6ebc0b5 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..45c68ee 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,10 @@ - + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 46cf136..7a20891 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,12 @@ -[package] -name = "comline" -version = "0.1.0" -edition = "2021" +[workspace] +# resolver = "2" +members = [ + "core", + "core_stdlib", + "package-manager", + "package-manager-server", + "runtime" +] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0.71" -once_cell = "1.17.1" -panik-handler = "0.1.0" -pest = "2.6.0" -pest_derive = "2.6.0" -rmp = "0.8.11" -rmp-serde = "1.1.1" -serde = "1.0.164" -serde_derive = "1.0.164" - - -[dev-dependencies] -pretty_assertions = "1.3.0" +[profile.release] +opt-level = 2 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index ee92c39..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 NewWars - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index b9f0d95..47f84a2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ # Comline -Comline(Communication on Line) is a library +## Development WOOS +This is a very temporary alias for development (which might just easily break), since we need +to use the package manager sometimes, lets do a alias: +### Linux +From this directory(`comline-rs/`, be sure you are here) in console, make an alias to the executable: + - Fish: `alias comline-dev="\"$(pwd)/target/debug/comline-package-manager\""` + - Bash: `alias comline-dev="\"$PWD/target/debug/comline-package-manager\""` (might be wrong, didn't test) -## Language details -Rust +Now the command `comline-dev` is available anywhere diff --git a/.gitattributes b/core/.gitattributes similarity index 100% rename from .gitattributes rename to core/.gitattributes diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 0000000..ad1ba3b --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,18 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + + +# Docs build +/docs/.cache/ diff --git a/core/.idea/.gitignore b/core/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/core/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/core/.idea/comline-rs.iml b/core/.idea/comline-rs.iml new file mode 100644 index 0000000..a6b108e --- /dev/null +++ b/core/.idea/comline-rs.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/.idea/modules.xml b/core/.idea/modules.xml new file mode 100644 index 0000000..3b5cb6d --- /dev/null +++ b/core/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/core/.idea/vcs.xml b/core/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/core/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..fdc22bf --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "comline" +version = "0.1.0" +edition = "2021" + +[dependencies] +eyre = "0.6.8" +once_cell = "1.17.1" +pest_derive = "2.6.0" +rmp = "0.8.11" +rmp-serde = "1.1.1" +serde = "1.0.164" +serde_derive = "1.0.164" + +pest = "2.6.0" +chumsky = "0.9.2" +ariadne = { version = "0.3.0", features = ["auto-color"] } +semver = "1.0.18" +blake3 = "1.4.1" +lz4_flex = "0.11.1" +lex = "0.6.0" +snafu = "0.7.5" +strum_macros = "0.25.1" +strum = { version = "0.25.0", features = ["derive"] } +downcast-rs = "1.2.0" +handlebars = "4.3.7" +regex = "1.9.3" +num-traits = "0.2.16" +num-derive = "0.4.0" + +[dev-dependencies] +pretty_assertions = "1.3.0" diff --git a/core/README.md b/core/README.md new file mode 100644 index 0000000..e3929a4 --- /dev/null +++ b/core/README.md @@ -0,0 +1,20 @@ +# Comline +Comline(Communication on Line) is a library + + + +## Language details +Rust + + +## Resource Links +https://createlang.rs/ +https://michael-f-bryan.github.io/static-analyser-in-rust/book/parse/parser.html +https://michael-f-bryan.github.io/static-analyser-in-rust/book/codemap.html +https://docs.rs/codemap/latest/codemap/ + + +## Resources of ideas and inspirations +https://www.youtube.com/watch?v=m64SWl9bfvk +https://www.youtube.com/watch?v=ApHpmA1k73k + diff --git a/core/examples/codegen/simple_gen.rs b/core/examples/codegen/simple_gen.rs new file mode 100644 index 0000000..e8bf754 --- /dev/null +++ b/core/examples/codegen/simple_gen.rs @@ -0,0 +1,32 @@ +// Standard Uses + +// Crate Uses + +// External Uses + +const EXPECTED_GENERATED_CODE: &str = r" +trait Registry { + fn register(message: Credentials) -> RegisterStatus; + fn my_username() -> String; + fn tell_back(message: String) -> Result; +} + +#[derive(ValidatorStringBounds<[u8; 8]>)] +struct Credentials { + +} + +struct RegisterStatus { + +} + +struct TellBackError { + +} +"; + + +fn main() { + todo!() +} + diff --git a/examples/dev.ids b/core/examples/dev.ids similarity index 78% rename from examples/dev.ids rename to core/examples/dev.ids index 2f8b419..2a4df0d 100644 --- a/examples/dev.ids +++ b/core/examples/dev.ids @@ -40,8 +40,6 @@ protocol Mail { // Maximum amount of articles that a provider can send const SENT_ARTICLES_MAX: u8 = 10 -//#provider=Server // TODO: This is the original proposal, it would only dictate direction, below is the new proposal -#provider=Multiple consumer=Multiple // TODO: These parameters need more situational introspection to be documented protocol Feed { // Fetches new articles since last fetch @flexible async stream diff --git a/examples/example.ids b/core/examples/example.ids similarity index 93% rename from examples/example.ids rename to core/examples/example.ids index d003cbb..28d384e 100644 --- a/examples/example.ids +++ b/core/examples/example.ids @@ -1,5 +1,3 @@ -// TODO: Think if the IDL should compile into a IR (Intermediate Representation) - // Identifier for this schema, every identifier should be unique, generated by the schema compiler // so its not necessary to do this manually tag 0563125512 @@ -61,10 +59,3 @@ protocol World provider=Server { // the caller will abort waiting for the response or return signal async client server function hello_back_both() -> str: timeout_ms=1000 } - - -// TODO: Think what »discoverable« should mean, just like Fuchsia IDL -@discoverable -protocol OpenWorld { -} - diff --git a/examples/greet.ids b/core/examples/greet.ids similarity index 100% rename from examples/greet.ids rename to core/examples/greet.ids diff --git a/examples/imports/ant.cls b/core/examples/imports/ant.cls similarity index 100% rename from examples/imports/ant.cls rename to core/examples/imports/ant.cls diff --git a/examples/imports/food.cls b/core/examples/imports/food.cls similarity index 100% rename from examples/imports/food.cls rename to core/examples/imports/food.cls diff --git a/examples/imports/hole.cls b/core/examples/imports/hole.cls similarity index 100% rename from examples/imports/hole.cls rename to core/examples/imports/hole.cls diff --git a/examples/simple.ids b/core/examples/simple.ids similarity index 78% rename from examples/simple.ids rename to core/examples/simple.ids index acdb4cc..0e57b22 100644 --- a/examples/simple.ids +++ b/core/examples/simple.ids @@ -1,16 +1,22 @@ -namespace examples.thing.simple +namespace examples::thing::simple const AGREEMENT_KEY: string = "agreement_key" -@min_chars=16 max_chars=60 validator PasswordCheck { - assert_valid( - self.parameters.0, - StringBounds(min_chars=params.min_chars max_chars=params.max_chars - ) + min_chars: u32 = 16 + max_chars: u32 = 60 + + validate = { + assert_valid( + self.parameters.0, + StringBounds(params.min_chars params.max_chars) + // StringBounds(min_chars=self.min_chars max_chars=self.max_chars) + ) + } } + @settings(versioned=False validate_fields=False) struct Credentials { diff --git a/core/proposals/enum_variant_associated_kind.ids b/core/proposals/enum_variant_associated_kind.ids new file mode 100644 index 0000000..7915638 --- /dev/null +++ b/core/proposals/enum_variant_associated_kind.ids @@ -0,0 +1,7 @@ +// TODO: Variants with fields need more thought to see if they can be implemented in languages +// TODO: that may not do it well, or at all +enum EncryptionMode { + None + // TODO: The »EncryptionAlgorithm« would be the kind for the »Encrypt« variant + Encrypt(EncryptionAlgorithm) +} diff --git a/core/proposals/protocol_function_blanket_impl.ids b/core/proposals/protocol_function_blanket_impl.ids new file mode 100644 index 0000000..e6570de --- /dev/null +++ b/core/proposals/protocol_function_blanket_impl.ids @@ -0,0 +1,8 @@ +// TODO: This proposal is about blanket implementations on protocol functions. +// TODO: What would go inside would be a block of expressions (the same used in a Validator) +// TODO: Statements could also exist but ONLY IF they really, really are necessary + +protocol TestBlanket { + function test_blank() { unimplemented() } + function test_blank_other() => StringBounds +} diff --git a/core/proposals/protocol_property_discoverable.ids b/core/proposals/protocol_property_discoverable.ids new file mode 100644 index 0000000..67bb3f6 --- /dev/null +++ b/core/proposals/protocol_property_discoverable.ids @@ -0,0 +1,5 @@ +// TODO: Think what »discoverable« should mean, just like Fuchsia IDL +@discoverable +protocol OpenWorld { +} + diff --git a/core/proposals/protocol_provider_and_provenance.ids b/core/proposals/protocol_provider_and_provenance.ids new file mode 100644 index 0000000..b6e99bd --- /dev/null +++ b/core/proposals/protocol_provider_and_provenance.ids @@ -0,0 +1,13 @@ +// TODO: This is the original proposal, it would only dictate direction, below is the new proposal +//#provider=Server + +// TODO: These parameters need more situational introspection to be documented +#provider=Multiple +#consumer=Multiple + +protocol Feed { + // Fetches new articles since last fetch + @flexible async stream + #timeout=1000ms per_send_timeout=10s cooldown=10s + 1# function fetch_articles() -> Article[SENT_ARTICLES_MAX] +} diff --git a/core/src/autodoc/document.rs b/core/src/autodoc/document.rs new file mode 100644 index 0000000..c296d6a --- /dev/null +++ b/core/src/autodoc/document.rs @@ -0,0 +1,44 @@ +// Standard Uses + +// Crate Uses +use crate::project::ir::diff::Differ; +use crate::project::ir::frozen::Dependency; + +// External Uses + + +#[allow(unused)] +#[derive(Debug)] +pub struct Document { + major_changes: Vec, + minor_changes: Vec +} +impl Document { + pub fn new() -> Box { + Box::new(Self { major_changes: vec![], minor_changes: vec![] }) + } +} + +impl Differ for Document { + fn on_namespace_changed(&mut self, old: &String, new: &String) { + self.major_changes.push( + format!("Package namespace changed from {} to {}", old, new) + ); + } + + fn on_specification_changed(&mut self, old: u8, new: u8) { + self.major_changes.push( + format!("Comline's specification version changed from {} to {}", old, new) + ) + } + + #[allow(unused)] + fn on_schema_paths_changed(&mut self, old: &Vec, new: &Vec) { + todo!() + } + + #[allow(unused)] + fn on_dependencies_changed(&mut self, old: &Vec, new: &Vec) { + todo!() + } +} diff --git a/core/src/autodoc/mod.rs b/core/src/autodoc/mod.rs new file mode 100644 index 0000000..1ff8e52 --- /dev/null +++ b/core/src/autodoc/mod.rs @@ -0,0 +1,50 @@ +// Relative Modules +pub mod package; +pub mod schema; +pub mod document; + +// Standard Uses + +// Crate Uses +use crate::schema::ir::frozen::unit::FrozenUnit; +use crate::schema::ir::diff; +use crate::schema::ir::diff::Differ; +use crate::autodoc::schema::Document; + +// External Uses + + + +#[allow(unused)] +pub fn document_differences(from: &Vec, to: &Vec) -> Document { + let mut listeners: Vec> = vec![Document::new()]; + + diff::match_differ(&mut listeners, from, to); + + *listeners.remove(0).downcast::().unwrap() +} + +/* +#[allow(unused)] +pub fn node_difference(from: FrozenUnit, to: FrozenUnit) { + for node in from { + match node { + FrozenUnit::Namespace(n) => {} + FrozenUnit::Import(_) => {} + FrozenUnit::Constant { .. } => {} + FrozenUnit::Property { .. } => {} + FrozenUnit::Parameter { .. } => {} + FrozenUnit::ExpressionBlock { .. } => {} + FrozenUnit::Enum { .. } => {} + FrozenUnit::EnumVariant(_) => {} + FrozenUnit::Settings { .. } => {} + FrozenUnit::Struct { .. } => {} + FrozenUnit::Protocol { .. } => {} + FrozenUnit::Function { .. } => {} + FrozenUnit::Error { .. } => {} + FrozenUnit::Validator { .. } => {} + FrozenUnit::Field { .. } => {} + } + } +} +*/ diff --git a/core/src/autodoc/package.rs b/core/src/autodoc/package.rs new file mode 100644 index 0000000..01ee861 --- /dev/null +++ b/core/src/autodoc/package.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/core/src/autodoc/schema.rs b/core/src/autodoc/schema.rs new file mode 100644 index 0000000..21f2455 --- /dev/null +++ b/core/src/autodoc/schema.rs @@ -0,0 +1,70 @@ +// Standard Uses + +// Crate Uses +use crate::schema::ir::diff::Differ; +use crate::schema::ir::frozen::unit::FrozenUnit; +use crate::schema::ir::compiler::interpreted::primitive::KindValue; + +// External Uses + + +#[allow(unused)] +#[derive(Debug)] +pub struct Document { + pub major_changes: Vec, + pub minor_changes: Vec, +} +#[allow(unused)] +impl Document { + pub fn new() -> Box { + Box::new(Self { major_changes: vec![], minor_changes: vec![] }) + } + + pub fn differ(&self, previous: &Vec, next: &Vec) { + todo!() + } +} + +#[allow(unused)] +impl Differ for Document { + fn on_namespace_changed(&mut self, old: &str, new: &str) { + self.major_changes.push( + format!("Namespace changed from '{}' to '{}'", old, new) + ); + } + + fn on_const_name_changed(&mut self, old: &str, new: &str) { + self.minor_changes.push( + format!("Constant name changed from '{}' to '{}'", old, new) + ); + } + + fn on_const_kind_changed(&mut self, old: u8, new: u8) { + todo!() + } + + fn on_const_default_value_changed( + &mut self, name: &str, left_kind_value: &KindValue, right_kind_value: &KindValue + ) { + let (left_kind_name, left_value_display) = left_kind_value.name_and_value(); + let (right_kind_name, right_value_display) = right_kind_value.name_and_value(); + + if left_kind_name != right_kind_name { + self.minor_changes.push( + format!( + "Constant '{}' kind changed from '{}' to '{}'", name, + left_kind_name, right_kind_name + ) + ) + } + + if left_value_display != right_value_display { + self.minor_changes.push( + format!( + "Constant '{}' default value changed from '{}' to '{}'", name, + left_value_display, right_value_display + ) + ) + } + } +} diff --git a/core/src/codegen/mod.rs b/core/src/codegen/mod.rs new file mode 100644 index 0000000..5dde353 --- /dev/null +++ b/core/src/codegen/mod.rs @@ -0,0 +1,28 @@ +// Relative Modules +pub mod rust; + +// Standard Uses +use std::collections::HashMap; + +// Crate Uses +use crate::schema::ir::frozen::unit::FrozenUnit; + +// External Uses +use once_cell::sync::Lazy; + + +pub type GeneratorFn = fn(&Vec) -> String; + +static GENERATORS: Lazy> = Lazy::new(|| { + HashMap::from([ + ("rust", rust::frozen_unit_to_code as _), + ]) +}); + +pub fn find_generator(name: &str) -> Option<&GeneratorFn> { + if !GENERATORS.contains_key(name) { + return None + } + + GENERATORS.get(name) +} diff --git a/core/src/codegen/rust.rs b/core/src/codegen/rust.rs new file mode 100644 index 0000000..5c3b038 --- /dev/null +++ b/core/src/codegen/rust.rs @@ -0,0 +1,102 @@ +// Standard Uses + +// Crate Uses +use crate::schema::ir::frozen::unit::FrozenUnit; + +// External Uses + + +#[allow(unused)] +pub fn frozen_unit_to_code(units: &Vec) -> String { + + "".to_owned() +} + +/* +fn unit_to_code(frozen_unit: FrozenUnit) -> String { + let mut code = String::new(); + + use FrozenUnit::*; + match frozen_unit { + Tag(_) => {} + Namespace(_) => {} + Import(_) => {} + Constant { .. } => {} + Property { .. } => {} + Parameter { .. } => {} + ExpressionBlock { .. } => {} + Enum { .. } => {} + EnumVariant(_) => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Error { .. } => {} + Validator { .. } => {} + _ => panic!("Reached forbidden or unimplemented node") + } + + code +} + +fn protocol_to_code(unit: FrozenUnit) -> String { + let FrozenUnit::Protocol { + docstring, parameters, name, functions + } = unit else { panic!() }; + + let mut code = String::new(); + + // Trait Start + // TODO: Maybe only include async_trait if at least one of the functions need it + code.push_str("#[async_trait]"); + code.push_str(&*format!("pub trait {} {{", name)); + + // Functions + for function in functions { + let FrozenUnit::Function { + docstring, name, synchronous, direction, + arguments, returns, throws + } = function else { panic!() }; + + let mut fn_code = String::new(); + + // Async definition (with async_trait) + if !synchronous { + fn_code.push_str("async "); + } + + // Fn + fn_code.push_str(&*format!("fn {} (", name)); + + // Arguments + let mut idx = 0; + while idx != arguments.len() { + let FrozenUnit::Parameter { + name, default_value + } = arguments.get(idx).unwrap(); + + if argument.id.is_none() { + fn_code.push_str(&*format!("arg{}: {:?}", idx, argument.type_)); + if idx != arguments.len() { fn_code.push_str(", ") } + } else { + fn_code.push_str( + &*format!("{}: {:?}", argument.id.clone().unwrap(), argument.type_) + ); + if idx != arguments.len() { fn_code.push_str(", ") } + } + + idx += 1; + } + + // Returns + + code.push_str(&*fn_code) + } + + // Trait End + code.push_str("}}"); + + code.to_owned() +} +*/ + diff --git a/core/src/langlib/lang_items.rs b/core/src/langlib/lang_items.rs new file mode 100644 index 0000000..ffcd537 --- /dev/null +++ b/core/src/langlib/lang_items.rs @@ -0,0 +1,17 @@ +// Standard Uses +use std::path::Path; + +// Local Uses +use crate::schema::idl::parser_new::from_path; +use crate::schema::idl::ast::unit::SourcedWhole; + +// External Uses +use once_cell::sync::Lazy; + + +pub static LANGUAGE_ITEMS: Lazy> = Lazy::new(|| + vec![ + from_path(Path::new("src/langlib/validators/string_bounds.ids")).unwrap() + ] +); + diff --git a/core/src/langlib/mod.rs b/core/src/langlib/mod.rs new file mode 100644 index 0000000..3e37a51 --- /dev/null +++ b/core/src/langlib/mod.rs @@ -0,0 +1,76 @@ +// Relative Modules +pub mod lang_items; +// pub mod validators; + +// Standard Uses + +// Local Uses +use crate::langlib::lang_items::LANGUAGE_ITEMS; +use crate::schema::idl::ast::unit::{ASTUnit, SpannedUnit}; + +// External Uses + + +pub fn find_unit<'a>(namespace: &str) -> Option<&'a Vec> { + let mut namespace_parts = namespace.split("::"); + + for (_, units) in LANGUAGE_ITEMS.iter() { + let unit_namespace = unit_namespace(units); + if unit_namespace.is_none() { continue } + let mut unit_namespace_parts = unit_namespace.unwrap().split("::"); + + loop { + let unit_part = unit_namespace_parts.next(); + let target_part = namespace_parts.next(); + + if target_part.is_none() { + return Some(units) + } + + if unit_part.is_none() { + let item = unit_item( + &units, target_part.unwrap() + ); + + if item.is_none() { continue } + + return Some(units) + } + + if target_part != unit_part { + return None + } + } + } + + None +} + +pub fn unit_namespace(unit: &Vec) -> Option<&str> { + for (_, variant) in unit { + return match variant { + ASTUnit::Namespace(_, n) => Some(n), + _ => None + } + } + + None +} + +pub fn unit_item<'a>(unit: &'a Vec, unit_name: &str) -> Option<&'a SpannedUnit> { + for spanned_unit in unit { + match &spanned_unit.1 { + ASTUnit::Constant { name: (_, name), .. } + | ASTUnit::Enum { name: (_, name), .. } + | ASTUnit::Settings { name: (_, name), .. } + | ASTUnit::Struct { name: (_, name), .. } + | ASTUnit::Error { name: (_, name), ..} + | ASTUnit::Validator { name: (_, name), ..} => { + if unit_name == name { return Some(spanned_unit) } + }, + _ => continue + } + } + + None +} diff --git a/src/lib.rs b/core/src/lib.rs similarity index 66% rename from src/lib.rs rename to core/src/lib.rs index daa0d2f..ae5dc48 100644 --- a/src/lib.rs +++ b/core/src/lib.rs @@ -8,7 +8,10 @@ extern crate serde_derive; extern crate rmp_serde as rmps; // Relative Modules -pub mod ir; pub mod codegen; -pub mod idl; -pub mod stdlib; +pub mod schema; +pub mod project; +pub mod autodoc; +pub mod utils; +pub mod report; +// pub mod langlib; diff --git a/core/src/project/build/mod.rs b/core/src/project/build/mod.rs new file mode 100644 index 0000000..af4902f --- /dev/null +++ b/core/src/project/build/mod.rs @@ -0,0 +1,170 @@ +// Standard Uses +use std::path::Path; +use std::collections::HashMap; +use std::fs::File; +use std::io::Write; +use std::rc::Rc; +use std::cell::RefCell; + +// Crate Uses +use crate::codegen; +use crate::codegen::GeneratorFn; +use crate::project::idl::constants::CONGREGATION_EXTENSION; +use crate::project::ir::interpreter::Interpreter as ProjectInterpreter; +use crate::project::ir::compiler::Compile; +use crate::project::ir::{compiler, diff, frozen}; +use crate::project::ir::frozen::{FrozenUnit, FrozenWhole}; +use crate::project::ir::frozen::meta::Index; +use crate::project::ir::context::ProjectContext; +use crate::schema::{idl, ir}; + +// External Uses +use handlebars::{Handlebars, RenderError}; +use eyre::Result; + + +pub fn build(path: &Path) -> Result<()> { + let config_path = path.join(format!("config.{}", CONGREGATION_EXTENSION)); + let config_path = config_path.as_path(); + + let frozen_path = path.join(".frozen/"); + + if !config_path.exists() { + panic!( + "Project directory {:?} has no configuration file {:?}", + path, config_path.file_name().unwrap() + ) + } + + let latest_frozen = frozen::loader::from_latest_frozen(&frozen_path); + + let compiled_project = ProjectInterpreter::from_origin(config_path); + + // TODO: Check integrity of the frozen contents, if they are valid, if something is broken, etc + + if let Some(latest_frozen) = latest_frozen { + diff::differ( + &latest_frozen, &compiled_project, + true, true + ); + } + + let mut project_ctx = RefCell::borrow_mut(&compiled_project.0); + for relative in frozen::schema_paths(&compiled_project.1) { + let concrete = format!("{:}/{}", path.to_str().unwrap(), relative); + let concrete_path = Path::new(&concrete); + + let unit = idl::parser_new::from_path(concrete_path)?; + + let context = Rc::new(RefCell::new( + ir::context::SchemaContext::with_project_and_main( + unit, compiled_project.0.clone() + ) + )); + + project_ctx.add_schema_context(context.clone()); + } + + compiler::interpret::interpret_context(&project_ctx)?; + + // We drop here since we should not need mutability anymore + drop(project_ctx); + + generate_code_for_targets(&compiled_project).unwrap(); + + Ok(()) +} + +pub fn generate_code_for_targets(compiled_project: &FrozenWhole) -> Result<()> { + for item in compiled_project.1.iter() { + match item { + FrozenUnit::CodeGeneration(details) => { + let Some((name, version)) = details.name.split_once("#") else { + panic!() + }; + + let args = Args { + default_path: "generated/{{language}}/{{version}}".to_owned(), + language: name.to_owned(), + version: version.to_owned(), + }; + + let path = resolve_path_query(&details.generation_path, args).unwrap(); + let path = Path::new(&path); + + let generator = codegen::find_generator(name); + + if generator.is_none() { + panic!( + "No generator found for language named {} with version {}", + name, version + ) + } + + generate_code_for_context( + &compiled_project.0.borrow(), generator, &path + )?; + }, + _ => {} + } + } + + Ok(()) +} + + +#[derive(Serialize, Deserialize)] +pub struct Args { + default_path: String, + language: String, + version: String +} + +pub fn resolve_path_query(query: &Option, args: Args) -> Result { + let mut reg = Handlebars::new(); + reg.set_strict_mode(true); + + if query.is_some() { + reg.render_template(&query.clone().unwrap(), &args) + } else { + reg.render_template(&args.default_path, &args) + } +} + +pub fn generate_code_for_context( + context: &ProjectContext, generator: Option<&GeneratorFn>, target_path: &Path +) -> Result<()> { + for schema_context in context.schema_contexts.iter() { + let ctx = schema_context.borrow(); + let frozen_schema = ctx.frozen_schema.as_ref().unwrap(); + + let mut code = "Generated code with Comline compiler".to_owned(); + + code += &*generator.unwrap()(frozen_schema); + + std::fs::write(target_path, code).unwrap(); + } + + Ok(()) +} + +pub fn build_index(frozen_path: &Path) { + let index = Index { versions: HashMap::from([ + ( + "0.0.1".to_owned(), + "d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1".to_owned() + ), + + ])}; + let result = index.to_processed(); + + let index_path = frozen_path.join("index"); + let mut file = File::create(index_path).unwrap(); + file.write(&*result.0.into_bytes()).unwrap(); + file.write(&*result.1).unwrap(); + file.flush().unwrap(); +} + +pub struct BuildOptions { + +} diff --git a/core/src/project/idl/ast.rs b/core/src/project/idl/ast.rs new file mode 100644 index 0000000..81e45e4 --- /dev/null +++ b/core/src/project/idl/ast.rs @@ -0,0 +1,49 @@ +// Standard Uses + +// Local Uses +use crate::utils::codemap::{CodeMap, Span}; + +// External Uses + + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum ASTUnit { + Namespace(Span, String), + Assignment { + name: (Span, String), + value: (Span, AssignmentUnit) + }, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum AssignmentUnit { + String(String), + Reference(String), + Number(u64), + DependencyList(Vec), + List(Vec), + Dictionary(Vec), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct DependencyListItem { + name: (Span, String), + author: (Span, String), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct DictKeyValue { + pub key: (Span, String), + pub value: (Span, AssignmentUnit) +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum ListItem { + Number(Span, u64), + String(Span, String), + Path(Span, String), +} + +pub type SpannedUnit = (Span, ASTUnit); +pub type SourcedWhole = (CodeMap, Vec); + diff --git a/core/src/project/idl/constants.rs b/core/src/project/idl/constants.rs new file mode 100644 index 0000000..38a6c80 --- /dev/null +++ b/core/src/project/idl/constants.rs @@ -0,0 +1,8 @@ +// Standard Uses + +// Local Uses + +// External Uses + + +pub const CONGREGATION_EXTENSION: &str = "idp"; diff --git a/core/src/project/idl/idc.pest b/core/src/project/idl/idc.pest new file mode 100644 index 0000000..668ab5d --- /dev/null +++ b/core/src/project/idl/idc.pest @@ -0,0 +1,130 @@ +// IDC grammar +syntax = _{ + COMMENT* ~ MULTILINE_COMMENT* + ~ congregation + ~ assignment* +} + +congregation = { + WS? ~ "congregation" + ~ WS ~ id +} + +path = _{ + WS? ~ (domain_namespaced | string) +} + +dependency_address = { + WS? ~ domain_namespaced + ~ WS? ~ "@" + ~ WS? ~ domain_namespaced +} + +assignment = { + WS? ~ ( + item_version_meta + | dependency_address + | domain_namespaced + ) + ~ WS? ~ "=" ~ WS? + ~ (number | string | list | dictionary) +} +list = { + WS? ~ "[" ~ WS? + ~ (string | number | path)* + ~ WS? ~ "]" ~ WS? +} +dictionary = { + WS? ~ "{" ~ WS? + ~ key_value* + ~ WS? ~ "}" ~ WS? +} +key_value = { + WS? ~ ( + item_version_meta | dependency_address + | domain + ) + ~ WS? ~ "=" + ~ WS? ~ ( + domain_namespaced | string + | dictionary | list + ) +} + +// Common Rules +item_version_meta = { + domain ~ "#" ~ version +} +version = { (number | id | ".")+ } +variable = @{ (id | kind | ".")+ } +domain = @{ (id | "::")+ } +domain_namespaced = @{ + (id | "::" | "_")+ +} +number = @{ digit+ } +id = @{ (alpha | "_")+ } +kind = @{ (alpha | digit)+ } + +instantiation = { + (domain | domain_namespaced) + ~ "(" ~ domain ~ ")" +} + +docstring = { + "///" ~ + (docstring_property | docstring_description) + ~ NEWLINE +} +docstring_property = { + " "* ~ "@" ~ " "* ~ domain + ~ " "* ~ ":" + ~ " "? ~ docstring_description +} +docstring_description = @{ + (!NEWLINE ~ ANY)+ +} + +value = { + "true" | "false" | number + | string | string_interpolated + | instantiation + | variable | domain | domain_namespaced +} + +any_string = { string | string_interpolated } + +string = _{ + "\"" ~ string_inner ~ "\"" +} +string_inner = @{ char* } + +string_interpolated = { + "f" ~ "\"" ~ string_interpolated_inner ~ "\"" +} +string_interpolated_inner = _{ + (string_interpolation | char)* +} +string_interpolation = _{ + "{" ~ domain ~ "}" +} + +char = { + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) +} + + +alpha = { 'a'..'z' | 'A'..'Z' } +digit = { '0'..'9' } + +WS = _{ (" " | "\t" | "\n")+ } +COMMENT = _{ + !"///" ~ + "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE +} +MULTILINE_COMMENT = _{ + "/*" + ~ (MULTILINE_COMMENT | !"*/" ~ ANY)* + ~ "*/" +} diff --git a/core/src/project/idl/mod.rs b/core/src/project/idl/mod.rs new file mode 100644 index 0000000..3baa023 --- /dev/null +++ b/core/src/project/idl/mod.rs @@ -0,0 +1,5 @@ +// Relative Modules +pub mod ast; +pub mod parser; +pub mod constants; +pub mod parser_new; diff --git a/core/src/project/idl/parser.rs b/core/src/project/idl/parser.rs new file mode 100644 index 0000000..2cae16d --- /dev/null +++ b/core/src/project/idl/parser.rs @@ -0,0 +1,47 @@ +// Standard Uses + +// Local Uses + +// External Uses + + + + +// TODO: This whole module is old and should be deleted, the module `parser_new` is the correct +// one, and should be renamed to just `parser` + +/* +pub fn from_pat\h(path: &Path) -> Result { + if !path.exists() { bail!("Path doesn't exist: {:?}", path) } + + from_path_str(path.to_str().unwrap()) +} + +pub fn from_path_str(path: &str) -> Result { + let raw = std::fs::read_to_string(path).unwrap(); + let vunit = parse_into_unit(raw.clone().as_str()).unwrap(); + let vindex = VIndex { + meta: UnitIndex::Index { path: path.to_string(), source: raw }, + nodes: vec![] + }; + + Ok((vindex, vunit)) +} + +pub fn parse_into_unit(content: &str) -> Result { + let pairs = IDLParser::parse(Rule::syntax, content)?; + let mut unit = vec![]; + + for pair in pairs { unit.push(parse_inner(pair).unwrap()) } + + Ok(unit) +} + +#[allow(unused)] +pub fn parse_inner(pair: Pair) -> Result { + match pair.as_rule() { + missing => { panic!("Rule not implemented: {:?}", missing) } + } +} + +*/ diff --git a/core/src/project/idl/parser_new.rs b/core/src/project/idl/parser_new.rs new file mode 100644 index 0000000..1d4fc4b --- /dev/null +++ b/core/src/project/idl/parser_new.rs @@ -0,0 +1,179 @@ +// Standard Uses +use std::path::Path; +use std::sync::Arc; + +// Crate Uses +use crate::project::idl::ast::{ + AssignmentUnit, ASTUnit, DictKeyValue, + ListItem, SourcedWhole, SpannedUnit +}; +use crate::utils::codemap::{CodeMap, FileMap}; + +// External Uses +use eyre::{bail, Result}; +use pest::{iterators::Pair, Parser}; + + +#[derive(Parser)] +#[grammar = "project/idl/idc.pest"] +pub struct ProjectParser; + +#[allow(unused)] +pub fn from_path(path: &Path) -> Result { + if !path.exists() { bail!("Path doesn't exist: {:?}", path) } + + let source = std::fs::read_to_string(path).unwrap(); + + let sourced_whole = parse_source( + source.clone(), + path.file_name().unwrap().to_str().unwrap().to_owned() + ); + + sourced_whole +} + + +pub fn parse_source(source: String, name: String) -> Result { + let mut codemap = CodeMap::new(); + let file = codemap.insert_file(name, source.clone()); + + let pairs = ProjectParser::parse(Rule::syntax, source.as_str())?; + let mut units = vec![]; + + for pair in pairs { + if let Ok(u) = parse_inner(pair, &file) { + units.push(u) + } + } + + Ok((codemap, units)) +} + +pub fn parse_inner(pair: Pair, file: &Arc) -> Result { + match pair.as_rule() { + Rule::congregation => { + let span = pair.as_span(); + let namespace_pair = pair.into_inner().next().unwrap(); + let namespace_span = namespace_pair.as_span(); + let namespace = namespace_pair.as_str().to_owned(); + + Ok(( + file.insert_span(span.start(), span.end()), + ASTUnit::Namespace( + file.insert_span(namespace_span.start(), namespace_span.end()), + namespace + ) + )) + }, + Rule::assignment => { + let span = pair.as_span(); + let mut inner = pair.into_inner(); + + let name_pair = inner.next().unwrap(); + let name_span = name_pair.as_span(); + + let value_pair = inner.next().unwrap(); + let value_span = value_pair.as_span(); + + Ok(( + file.insert_span(span.start(), span.end()), + ASTUnit::Assignment { + name: ( + file.insert_span(name_span.start(), name_span.end()), + name_pair.as_str().to_owned() + ), + value: ( + file.insert_span(value_span.start(), value_span.end()), + parse_assignment(value_pair, file)? + ) + } + )) + } + missing => panic!("Rule not implemented {:?}", missing) + // _ => { bail!("")} + } +} + + +#[allow(unused)] +fn parse_assignment(pair: Pair, file: &Arc) -> Result { + let unit = match pair.as_rule() { + Rule::number => { + Ok(AssignmentUnit::Number(pair.as_str().parse().unwrap())) + }, + Rule::string => { + Ok(AssignmentUnit::String(pair.as_str().to_owned())) + }, + Rule::dictionary => { + let span = pair.as_span(); + let span = file.insert_span(span.start(), span.end()); + + let mut key_values = vec![]; + + for item in pair.into_inner() { + let mut inner = item.into_inner(); + + let key_pair = inner.next().unwrap(); + let key_span = key_pair.as_span(); + let key_span = file.insert_span(key_span.start(), key_span.end()); + let key = key_pair.as_str().to_owned(); + + let value_pair = inner.next().unwrap(); + let value_span = value_pair.as_span(); + let value_span = file.insert_span(value_span.start(), value_span.end()); + let value = parse_assignment(value_pair, file)?; + + key_values.push(DictKeyValue { + key: (key_span, key), + value: (value_span, value) + }); + } + + Ok(AssignmentUnit::Dictionary(key_values)) + } + Rule::list => { + let span = pair.as_span(); + let span = file.insert_span(span.start(), span.end()); + + let mut items = vec![]; + + for item in pair.into_inner() { + items.push(parse_list_item(item, file)?); + } + + Ok(AssignmentUnit::List(items)) + }, + Rule::domain_namespaced => { + Ok(AssignmentUnit::String(pair.as_str().to_owned())) + }, + Rule::string_inner => { + Ok(AssignmentUnit::String(pair.as_str().to_owned())) + }, + missing => panic!("Rule not implemented: {:?}", missing) + }; + + unit +} + + +fn parse_list_item(pair: Pair, file: &Arc) -> Result { + let span = pair.as_span(); + let item_span = file.insert_span(span.start(), span.end()); + + match pair.as_rule() { + Rule::number => { + Ok(ListItem::Number(item_span, pair.as_str().parse().unwrap())) + }, + Rule::string_inner => { + Ok(ListItem::String(item_span, pair.as_str().to_owned())) + }, + Rule::path => { + Ok(ListItem::Path(item_span, pair.as_str().to_owned())) + }, + Rule::domain_namespaced => { + Ok(ListItem::String(item_span, pair.as_str().to_owned())) + }, + missing => panic!("Rule not implemented: {:?}", missing) + } +} + diff --git a/core/src/project/ir/compiler/interpret.rs b/core/src/project/ir/compiler/interpret.rs new file mode 100644 index 0000000..a957afc --- /dev/null +++ b/core/src/project/ir/compiler/interpret.rs @@ -0,0 +1,27 @@ +// Standard Uses +use std::cell::RefMut; + +// Crate Uses +use crate::project::ir::context::ProjectContext; +use crate::schema::ir::compiler::interpreted::interpreter; +use crate::schema::ir::compiler::interpreter::meta_stage; + +// External Uses +use eyre::{anyhow, Result}; + + +pub fn interpret_context(project_context: &RefMut) -> Result<()> { + for schema_context in project_context.schema_contexts.iter() { + meta_stage::compile_schema_metadata(schema_context, project_context) + .map_err(|e| anyhow!("{}", e))?; + } + + /* + for schema_context in project_context.schema_contexts.iter() { + interpreter::interpret_context(schema_context, project_context) + .map_err(|e| anyhow!("{}", e))?; + } + */ + + Ok(()) +} diff --git a/core/src/project/ir/compiler/mod.rs b/core/src/project/ir/compiler/mod.rs new file mode 100644 index 0000000..552172a --- /dev/null +++ b/core/src/project/ir/compiler/mod.rs @@ -0,0 +1,25 @@ +// Relative Modules +pub mod interpret; +pub mod report; + +// Standard Uses +use std::path::Path; + +// Crate Uses +use crate::project::idl::ast::{ASTUnit, SourcedWhole}; + +// External Uses + + +pub trait Compile { + type Output; + + fn from_ast(ast: Vec) -> Self::Output; + + fn from_sourced_whole(sourced: SourcedWhole) -> Self::Output; + + fn from_source(source: &str) -> Self::Output; + + fn from_origin(origin: &Path) -> Self::Output; +} + diff --git a/core/src/project/ir/compiler/report.rs b/core/src/project/ir/compiler/report.rs new file mode 100644 index 0000000..fb92696 --- /dev/null +++ b/core/src/project/ir/compiler/report.rs @@ -0,0 +1,39 @@ +// Standard Uses +use std::io; + +// Crate Uses + +// External Uses +use snafu::Snafu; + + +#[repr(u16)] +#[derive(Debug, Snafu)] +#[snafu(visibility(pub(crate)))] +pub enum CompileError { + // #[strum(props(Id="2"))] + #[snafu(display("Found namespace '{target}' when its already declared in '{origin}'"))] + Namespace { origin: String, target: String }, + + // #[strum(props(Id="2"))] + #[snafu(display("Spec version was not assigned'"))] + // SpecVersionNotTold { source: Box } + SpecVersionNotTold { source: io::Error }, + + // #[strum(props(Id="2"))] + #[snafu(display("Schema paths list is empty, at least one schema is required'"))] + // SpecVersionNotTold { source: Box } + SchemaPathsEmpty, +} + +/* +impl From for Error { + fn from(e: CompilerError) -> Box { + super::Error::IO { + source: Box::new(e), + } + } +} +*/ + +pub type CompileResult = std::result::Result; diff --git a/core/src/project/ir/context.rs b/core/src/project/ir/context.rs new file mode 100644 index 0000000..502c31a --- /dev/null +++ b/core/src/project/ir/context.rs @@ -0,0 +1,117 @@ +use std::cell::RefCell; +// Standard Uses +use std::path::PathBuf; +use std::rc::Rc; + +// Crate Uses +use crate::project::idl::ast::{SourcedWhole as ProjectSourcedWhole}; +use crate::schema::ir::context::SchemaContext; +use crate::schema::idl::ast::unit::{ASTUnit, Details}; + +// External Uses + + +#[derive(Clone)] +pub enum Origin { + Virtual, + Disk(PathBuf) +} + +#[derive(Clone)] +pub struct ProjectContext { + pub origin: Origin, + pub config: ProjectSourcedWhole, + pub schema_contexts: Vec>>, + + pub relative_projects: Vec>, +} + + +#[allow(unused)] +impl ProjectContext { + pub fn with_config_origin(origin: Origin, config: ProjectSourcedWhole) -> Self { + Self { + origin, config, + relative_projects: vec![], + schema_contexts: vec![], + } + } + + pub fn with_config(config: ProjectSourcedWhole) -> Self { + Self { + origin: Origin::Virtual, config, + relative_projects: vec![], + schema_contexts: vec![], + } + } + + pub(crate) fn add_relative_project(mut self, sourced: ProjectSourcedWhole) { + self.relative_projects.push( + Rc::from(Self::with_config(sourced)) + ) + } + + pub(crate) fn add_relative_project_context(mut self, context: Rc) { + todo!() + } + + pub(crate) fn add_schema_context(&mut self, context: Rc>) { + self.schema_contexts.push(context); + } + + pub(crate) fn sanitize_units(self) { + todo!() + } + + pub(crate) fn find_schema_by_import(&self, import: &str) -> Option<&Rc>> { + for schema_context in self.schema_contexts.iter() { + let ctx = schema_context.borrow(); + let units = &ctx.schema.1; + + if let Some(unit) = units.find_namespace() { + if let ASTUnit::Namespace(_, namespace) = &unit.1 { + if namespace == import { + return Some(schema_context) + } + } + } + } + + None + } + + /* + pub(crate) fn find_whole_unit_by_import(&self, import: &str) -> Option<&WholeUnit> { + if self.include_stdlib { + if let Some(stdlib_unit) = langlib::find_unit(import) { + return Some(stdlib_unit) + } + } + + None + } + */ + + /* + pub(crate) fn find_schema_context(&self, sub_namespace: &str) -> Option> { + todo!() + } + */ + + pub(crate) fn find_relative_project_context(&self, import: &str) -> Option<&ProjectContext> { + todo!() + } + + pub(crate) fn find_schema_by_filename(&self, filename: &String) -> Option { + let Origin::Disk(origin) = &self.origin else { + panic!("Only disk lookups are supported at the moment") + }; + + let schema_location = origin.with_file_name(filename); + + if schema_location.exists() { return Some(schema_location) } + + None + } +} + diff --git a/core/src/project/ir/diff/mod.rs b/core/src/project/ir/diff/mod.rs new file mode 100644 index 0000000..d786ed3 --- /dev/null +++ b/core/src/project/ir/diff/mod.rs @@ -0,0 +1,117 @@ +// Relative Modules +pub mod report; +pub mod versioning; + +// Standard Uses +use std::fmt::Debug; + +// Crate Uses +use crate::autodoc::document::Document; +use crate::project::ir::frozen::{Dependency, FrozenUnit, FrozenWhole}; +use crate::project::ir::diff::versioning::Versioning; + +// External Uses +use semver::Version; +use downcast_rs::{Downcast, impl_downcast}; + + +pub trait Differ: Downcast + Debug { + fn on_namespace_changed(&mut self, old: &String, new: &String); + fn on_specification_changed(&mut self, old: u8, new: u8); + fn on_schema_paths_changed(&mut self, old: &Vec, new: &Vec); + fn on_dependencies_changed(&mut self, old: &Vec, new: &Vec); + + // fn on_assignment_changed(&self, old: AssignmentUnit, new: AssignmentUnit); + + fn differ( + &self, previous: &Vec, next: &FrozenWhole, + document_gen: bool, auto_version: bool + ) { + differ(previous, next, document_gen, auto_version) + } +} +impl_downcast!(Differ); + +#[allow(unused)] +pub fn differ( + previous: &Vec, next: &FrozenWhole, + document_gen: bool, auto_version: bool +) { + let mut previous_version = versioning::version_from(previous).unwrap_or( + &Version::parse("0.0.0").unwrap().to_string() + ); + + let mut listeners: Vec> = vec![]; + + if document_gen { listeners.push(Document::new()) } + if auto_version { listeners.push(Versioning::new()) } + + match_differ(&mut listeners, previous, next); + + let document = listeners[0].downcast_ref::().unwrap(); + let versioning = listeners[1].downcast_ref::().unwrap(); +} + +#[allow(unused)] +pub fn match_differ( + mut listeners: &mut Vec>, + previous: &Vec, next: &FrozenWhole +) { + for node in previous { + use FrozenUnit::*; + match node { + Namespace(_) => { + search_other(node, &next.1).map(|l| { + let Namespace(old) = node else { panic!() }; + let Namespace(new) = l else { panic!() }; + + if old != new { + listeners.iter_mut().for_each(|mut l| l.on_namespace_changed( + old, new + )) + } + }); + } + SpecificationVersion(_) => { + search_other(node, &next.1).map(|l| { + let SpecificationVersion(old) = node else { panic!() }; + let SpecificationVersion(new) = l else { panic!() }; + + if old != new { + listeners.iter_mut().for_each(|mut l| + l.on_specification_changed(*old, *new) + ) + } + }); + } + SchemaPath(_) => {} + Dependency(_) => {} + missing => panic!("Node not implemented: {:?}", missing) + } + } +} + + +pub fn search_other<'a>(node: &FrozenUnit, others: &'a Vec) -> Option<&'a FrozenUnit> +{ + for other in others { + use FrozenUnit::*; + match node { + Namespace(_) => { + if let Namespace(_) = other { return Some(other) }; + } + SpecificationVersion(_) => { + if let SpecificationVersion(_) = other { return Some(other) }; + } + SchemaPath(_) => { + if let SchemaPath(_) = other { return Some(other) }; + } + Dependency(_) => { + if let SchemaPath(_) = other { return Some(other) }; + } + missing => { panic!("Node not implemented: {:?}", missing) } + } + } + + None +} diff --git a/core/src/project/ir/diff/report.rs b/core/src/project/ir/diff/report.rs new file mode 100644 index 0000000..b2ec508 --- /dev/null +++ b/core/src/project/ir/diff/report.rs @@ -0,0 +1,30 @@ +// Standard Uses +use std::io; + +// Crate Uses + +// External Uses +use snafu::Snafu; + + +#[repr(u16)] +#[derive(Debug, Snafu)] +#[snafu(visibility(pub(crate)))] +pub enum DiffError { + // #[strum(props(Id="2"))] + #[snafu(display("Found namespace '{target}' when its already declared in '{origin}'"))] + Namespace { origin: String, target: String }, + + // #[strum(props(Id="2"))] + #[snafu(display("Spec version was not assigned'"))] + // SpecVersionNotTold { source: Box } + SpecVersionNotTold { source: io::Error }, + + // #[strum(props(Id="2"))] + #[snafu(display("Schema paths list is empty, at least one schema is required'"))] + // SpecVersionNotTold { source: Box } + SchemaPathsEmpty, +} + + +// pub type CompileResult = std::result::Result; diff --git a/core/src/project/ir/diff/versioning.rs b/core/src/project/ir/diff/versioning.rs new file mode 100644 index 0000000..89f32e7 --- /dev/null +++ b/core/src/project/ir/diff/versioning.rs @@ -0,0 +1,60 @@ +// Standard Uses + +// Crate Uses +use crate::project::ir::diff::Differ; +use crate::project::ir::frozen::{Dependency, FrozenUnit}; + +// External Uses + + +#[allow(unused)] +#[derive(Debug)] +pub struct Versioning { + bump_major: bool, + bump_minor: bool, + bump_patch: bool +} +impl Versioning { + pub fn new() -> Box { + Box::new(Self { bump_major: false, bump_minor: false, bump_patch: false }) + } +} + +impl Differ for Versioning { + fn on_namespace_changed(&mut self, _: &String, _: &String) { + self.bump_major = true; + } + + #[allow(unused)] + /// Changing the specification is considered a major change, because + /// different specifications have different critical structure changes normally + fn on_specification_changed(&mut self, old: u8, new: u8) { + self.bump_major = true; + } + + #[allow(unused)] + fn on_schema_paths_changed(&mut self, old: &Vec, new: &Vec) { + self.bump_minor = true; + } + + #[allow(unused)] + fn on_dependencies_changed(&mut self, old: &Vec, new: &Vec) { + self.bump_minor = true; + } +} + +pub fn change() { + // use semver::Version; + // version: Version, +} + + +pub fn version_from(nodes: &Vec) -> Result<&String, ()> { + for node in nodes { + match node { + _ => {} + } + } + + Err(()) +} diff --git a/core/src/project/ir/frozen/loader.rs b/core/src/project/ir/frozen/loader.rs new file mode 100644 index 0000000..f948388 --- /dev/null +++ b/core/src/project/ir/frozen/loader.rs @@ -0,0 +1,66 @@ +// Standard Uses +use std::path::{Path, PathBuf}; + +// Crate Uses +use crate::project::ir::frozen::{FrozenUnit, FrozenWhole, meta}; + +// External Uses +use eyre::Result; + + +#[allow(unused)] +pub fn from_frozen_origin(path: &Path) -> Option { + // let context = from_frozen_objects(&project_path); + + todo!() +} + +#[allow(unused)] +pub fn from_frozen_objects(path: &PathBuf) -> Result> { + todo!() +} + +pub fn from_latest_frozen(path: &PathBuf) -> Option> { + let project_path = path.join("objects/"); + + if !project_path.exists() { return None } + + let projects = all_from_origin(&project_path).unwrap(); + + projects.first().cloned() +} + +/// Load all project frozen units from a frozen origin projects directory +pub fn all_from_origin(path: &PathBuf) -> Result>> { + let frozen_projects_path = path.join("project/"); + let path_items = std::fs::read_dir(frozen_projects_path)?; + + let mut project_metas = vec![]; + + for file in path_items { + let path = file?.path(); + + if !path.is_file() { panic!("Found something that isn't a file and shouldn't be here") } + + project_metas.push(meta::from_project_meta(&path)?); + } + + Ok(project_metas) +} + + + +/* +let val = vec![ + FrozenUnit::Namespace("test".to_owned()), + FrozenUnit::SpecificationVersion(1), + FrozenUnit::SchemaPath("thing.ids".to_owned()), + FrozenUnit::Dependency(Dependency { + author: "test".to_owned(), + project: "test".to_owned(), + version: "test".to_owned(), + }) +]; + +std::fs::write(path, to_processed(val).1)?; +*/ diff --git a/core/src/project/ir/frozen/meta.rs b/core/src/project/ir/frozen/meta.rs new file mode 100644 index 0000000..39fcd6e --- /dev/null +++ b/core/src/project/ir/frozen/meta.rs @@ -0,0 +1,67 @@ +// Standard Uses +use std::path::{Path, PathBuf}; +use std::collections::HashMap; + +// Crate Uses +use crate::project::ir::frozen::FrozenUnit; + +// External Uses +use eyre::{anyhow, Result}; +use rmps::Serializer; +use serde::Serialize; + + +#[derive(Serialize, Deserialize)] +pub struct Index { + pub versions: HashMap, +} +impl Index { + pub fn from_project_meta_index(path: &Path) -> Result { + let filename = path.file_name().unwrap().to_str().unwrap(); + let contents = std::fs::read(path)?; + let hash = blake3::hash(&*contents); + + if hash.to_string() != filename { + panic!( + "Project meta file name '{}' does not match its content's hash '{}'", + filename, hash + ); + } + + rmp_serde::from_slice(&*contents).map_err(|e| anyhow!(e)) + } + + pub fn to_processed(&self) -> (String, Vec) { + let mut output = vec![]; + self.serialize(&mut Serializer::new(&mut output)).unwrap(); + let hash = blake3::hash(&*output); + + (hash.to_string(), output) + } +} + + +pub fn from_project_meta(path: &PathBuf) -> Result> { + let filename = path.file_name().unwrap().to_str().unwrap(); + let contents = std::fs::read(path)?; + let hash = blake3::hash(&*contents); + + if hash.to_string() != filename { + panic!( + "Project meta file name '{}' does not match its content's hash '{}'", + filename, hash + ); + } + + let deserialized: Vec = rmp_serde::from_slice(&*contents)?; + + Ok(deserialized) +} + +pub fn to_processed(units: Vec) -> (String, Vec) { + let mut output = vec![]; + units.serialize(&mut Serializer::new(&mut output)).unwrap(); + let hash = blake3::hash(&*output); + + (hash.to_string(), output) +} diff --git a/core/src/project/ir/frozen/mod.rs b/core/src/project/ir/frozen/mod.rs new file mode 100644 index 0000000..aaa4bcc --- /dev/null +++ b/core/src/project/ir/frozen/mod.rs @@ -0,0 +1,61 @@ +// Relative Modules +pub mod loader; +pub mod meta; + +// Standard Uses +use std::rc::Rc; +use std::cell::RefCell; + +use std::slice::Iter; +use std::iter::FilterMap; + +// Crate Uses +use crate::project::ir::context::ProjectContext; + +// External Uses + + +pub type FrozenWhole = (Rc>, Vec); + + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +pub enum FrozenUnit { + Namespace(String), + SpecificationVersion(u8), + SchemaPath(String), + Dependency(Dependency), + CodeGeneration(LanguageDetails), + PublishTarget(PublishTarget) +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +pub struct Dependency { + pub(crate) author: String, + pub(crate) project: String, + pub(crate) version: String +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +pub struct LanguageDetails { + pub(crate) name: String, + pub(crate) versions: Vec, + pub(crate) generation_path: Option +} + +#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] +pub struct PublishTarget { + pub(crate) name: String, + pub(crate) url: String, + pub(crate) auth_method: String +} + +pub fn schema_paths(units: &Vec) + -> FilterMap, fn(&FrozenUnit) -> Option<&str>> +{ + units.iter().filter_map(|unit| { + match unit { + FrozenUnit::SchemaPath(path) => Some(path), + _ => None + } + }) +} diff --git a/core/src/project/ir/interpreter/freezing.rs b/core/src/project/ir/interpreter/freezing.rs new file mode 100644 index 0000000..19fc768 --- /dev/null +++ b/core/src/project/ir/interpreter/freezing.rs @@ -0,0 +1,212 @@ +// Standard Uses + +// Crate Uses +use crate::project::idl::ast::{AssignmentUnit, ASTUnit, DictKeyValue, ListItem}; +use crate::project::ir::compiler::report::CompileError; +use crate::project::ir::context::ProjectContext; +use crate::project::ir::frozen::{FrozenUnit, FrozenWhole, LanguageDetails, PublishTarget}; +use crate::report::ReportDetails; +use crate::utils::codemap::Span; + +// External Uses + + +#[allow(unused)] +pub fn interpret_node_into_frozen(context: &ProjectContext, node: &ASTUnit) + -> Result, ReportDetails> +{ + // use crate::project::idl::ast::ASTUnit::*; + match node { + ASTUnit::Namespace(span, name) => { + Ok(vec![FrozenUnit::Namespace(name.clone())]) + }, + ASTUnit::Assignment {name, value} => { + interpret_assignment(context, name,value) + }, + missing => unimplemented!("AST Node not implemented '{:?}'", missing) + } +} + +pub fn interpret_assignment( + context: &ProjectContext, name: &(Span, String), node: &(Span, AssignmentUnit) +) -> Result, ReportDetails> { + let result = match &*name.1 { + "specification_version" => { + let AssignmentUnit::Number(version) = &node.1 else { + panic!( + "'specification_version' should be a number(up to unsigned long integer, \ + got something else instead." + ) + }; + + vec![FrozenUnit::SpecificationVersion(*version as u8)] + }, + "schema_paths" => { + let AssignmentUnit::List(paths) = &node.1 else { + panic!("'schema_paths' should be a list of paths, got something else instead.") + }; + + let mut solved = vec![]; + for path in paths { + let ListItem::String(.., path) = path else { + panic!("Expected path, got something else instead") + }; + + let schema_file = context.find_schema_by_filename(path); + + if schema_file.is_none() { panic!("No schema found with the path: '{}'", path) } + + solved.push(FrozenUnit::SchemaPath(path.clone())); + } + + solved + }, + "code_generation" => { + let AssignmentUnit::Dictionary(items) = &node.1 else { + panic!("Expected dictionary, got something else instead") + }; + + interpret_assignment_code_generation(items)? + }, + "publish_targets" => { + let AssignmentUnit::Dictionary(items) = &node.1 else { + panic!("Expected dictionary, got something else instead") + }; + + interpret_assigment_publish_targets(items)? + }, + any => { + panic!("'{}' is not a valid assignment", any) + } + }; + + Ok(result) +} + +fn interpret_assignment_code_generation(items: &Vec) + -> Result, ReportDetails> +{ + let mut languages = vec![]; + + use AssignmentUnit::*; + for kv in items { + let key = &kv.key; + let Dictionary(value) = &kv.value.1 else { + panic!("Not expected") + }; + + match &*key.1 { + "languages" => { + for lang_details in value { + let name = &lang_details.key; + let Dictionary(details) = &lang_details.value.1 else { + panic!("Not expected here") + }; + + let mut versions = vec![]; + let path = None; + + for assignment in details { + match &*assignment.key.1 { + "package_versions" => { + let List(items) = &assignment.value.1 else { + panic!("Wrong kind") + }; + + for item in items { + let ListItem::String(_, version) = item else { + panic!("Not kind") + }; + + versions.push(version.clone()) + + } + }, + other => { panic!("Not expected another: {:?}", other) } + } + } + + languages.push( + FrozenUnit::CodeGeneration( + LanguageDetails { + name: name.1.clone(), versions, + generation_path: path, + } + ) + ); + } + }, + other => panic!("Key not allowed here: {}", other) + } + } + + Ok(languages) +} + +fn interpret_assigment_publish_targets(items: &Vec) + -> Result, ReportDetails> +{ + let mut targets = vec![]; + + use AssignmentUnit::*; + for kv in items { + let key = &kv.key; + + let target = match &kv.value.1 { + String(name) => { + // TODO: We might only need reference to variables, a string wouldn't be much useful + FrozenUnit::PublishTarget(PublishTarget { + name: name.clone(), + url: "none".to_string(), + auth_method: "none".to_string(), + }) + } + Reference(_reference) => { + panic!() + } + Dictionary(items) => { + let mut url = None; + let mut method = None; + + for item in items { + match &*item.key.1 { + "url" => { + if let String(s) = &item.value.1 { + url = Some(s); + } else { panic!() }; + }, + "method" => { + if let String(s) = &item.value.1 { + method = Some(s); + } else { panic!() }; + }, + other => panic!("Key not allowed here: {}", other) + } + } + + FrozenUnit::PublishTarget(PublishTarget { + name: key.1.clone(), + url: url.unwrap().clone(), + auth_method: method.unwrap().clone(), + }) + }, + other => panic!( + "Can only be a reference or a dict, got {:?} instead", other + ) + }; + + targets.push(target); + } + + Ok(targets) +} + +#[allow(unused)] +pub fn into_frozen_whole( + context: &ProjectContext, interpreted: Vec +) -> Result> +{ + todo!() + // Ok((Rc::from(context), interpreted)) +} + diff --git a/core/src/project/ir/interpreter/interpret.rs b/core/src/project/ir/interpreter/interpret.rs new file mode 100644 index 0000000..d03295b --- /dev/null +++ b/core/src/project/ir/interpreter/interpret.rs @@ -0,0 +1,32 @@ +// Standard Uses + +use std::cell::RefCell; +use std::rc::Rc; +// Crate Uses +use crate::project::ir::context::ProjectContext; +use crate::project::ir::frozen::{FrozenUnit, FrozenWhole}; +use crate::project::ir::compiler::report::CompileError; +use crate::project::ir::interpreter::freezing; +use crate::report::ReportDetails; + +// External Uses + + +#[allow(unused)] +pub fn interpret_context(context: ProjectContext) + -> Result> +{ + let mut interpreted: Vec = vec![]; + + for node in &context.config.1 { + let file = context.config.0.files().first().unwrap(); + let span = file.range_of(node.0).unwrap(); + + interpreted.append( + &mut freezing::interpret_node_into_frozen(&context, &node.1)? + ); + } + + Ok((Rc::new(RefCell::new(context)), interpreted)) + // freezing::into_frozen_whole(&context, interpreted) +} diff --git a/core/src/project/ir/interpreter/mod.rs b/core/src/project/ir/interpreter/mod.rs new file mode 100644 index 0000000..71a1c2b --- /dev/null +++ b/core/src/project/ir/interpreter/mod.rs @@ -0,0 +1,57 @@ +// Relative Modules +pub mod report; +pub mod interpret; +pub mod freezing; + +// Standard Uses +use std::path::Path; + +// Crate Uses +use crate::project; +use crate::project::idl::parser_new; +use crate::project::idl::ast::{ASTUnit, SourcedWhole}; +use crate::project::ir::context::{Origin, ProjectContext}; +use crate::project::ir::frozen::FrozenWhole; +use crate::project::ir::compiler::Compile; + +// External Uses + + +#[allow(unused)] +pub struct Interpreter { + context: ProjectContext +} + +#[allow(unused)] +impl Compile for Interpreter { + type Output = FrozenWhole; + + fn from_ast(ast: Vec) -> Self::Output { + todo!() + } + + fn from_sourced_whole(sourced: SourcedWhole) -> Self::Output { + let context = ProjectContext::with_config(sourced); + + interpret::interpret_context(context).unwrap() + } + + fn from_source(source: &str) -> Self::Output { + println!("Compiling source: {}", source); + let ast = parser_new::parse_source( + source.to_owned(), "".to_owned() + ).unwrap(); + + // println!("{:?}", ast); + Self::from_sourced_whole(ast) + } + + fn from_origin(origin: &Path) -> Self::Output { + let sourced = project::idl::parser_new::from_path(&origin).unwrap(); + let context = ProjectContext::with_config_origin( + Origin::Disk(origin.to_path_buf()), sourced + ); + + interpret::interpret_context(context).unwrap() + } +} diff --git a/core/src/project/ir/interpreter/report.rs b/core/src/project/ir/interpreter/report.rs new file mode 100644 index 0000000..1e5238a --- /dev/null +++ b/core/src/project/ir/interpreter/report.rs @@ -0,0 +1,50 @@ +// Standard Uses + +// Crate Uses +use crate::schema::ir::compiler::report::CompileWarning; +use crate::project::idl::ast::AssignmentUnit; +use crate::project::ir::compiler::report::CompileResult; +use crate::project::ir::frozen::Dependency; +use crate::report::ReportDetails; + +// External Uses + + +#[allow(unused)] +pub fn report_errors( + spec_version: Option, schema_paths: Option>, + dependencies: Option>, assignments: Vec +) -> CompileResult<()> { + // let mut errors = vec![]; + + // let spec_version = spec_version.context(report::SpecVersionNotToldSnafu); + // let spec_version = spec_version.context(report::SpecVersionNotToldSnafu); + // spec_version.unwrap(); + + Ok(()) + + // spec_version.context(|e| report::SpecVersionNotToldSnafu ); + + /* + if spec_version.is_none() { + ensure!( + spec_version.is_none(), + ReportDetails { kind: CompileError::SpecVersionNotTold, span: None } + ); + errors.push(); + } + */ + + // errors +} + +#[allow(unused)] +pub fn report_warnings( + spec_version: Option, schema_paths: Option>, + dependencies: Option>, assignments: Vec +) -> Vec> { + let warnings = vec![]; + + + warnings +} diff --git a/core/src/project/ir/mod.rs b/core/src/project/ir/mod.rs new file mode 100644 index 0000000..6aee1d4 --- /dev/null +++ b/core/src/project/ir/mod.rs @@ -0,0 +1,14 @@ +// Relative Modules +pub mod compiler; +pub mod interpreter; +pub mod context; +pub mod frozen; +pub mod diff; + +// Standard Uses + +// Local Uses + +// External Uses + + diff --git a/core/src/project/mod.rs b/core/src/project/mod.rs new file mode 100644 index 0000000..a615483 --- /dev/null +++ b/core/src/project/mod.rs @@ -0,0 +1,41 @@ +// Relative Modules +pub mod idl; +pub mod ir; +pub mod build; + +// Standard Uses + +// Local Uses +use crate::project::ir::frozen::Dependency; + +// External Uses + + +pub struct Project { + // pub configuration: WholeUnit +} + +impl Project { + /* + pub fn from_config_path(path: &Path) -> Self { + Self { + configuration: parser::from_path(path).unwrap(), + } + } + */ + + pub fn dependencies<'a>(self) -> &'a Vec { + todo!() + + /* + for node in self.configuration.1 { + match node { + ASTUnit::Congregation(_) => {} + ASTUnit::SpecificationVersion(_) => {} + ASTUnit::Schemas(_) => {} + } + } + */ + } +} + diff --git a/core/src/report.rs b/core/src/report.rs new file mode 100644 index 0000000..21e7f58 --- /dev/null +++ b/core/src/report.rs @@ -0,0 +1,54 @@ +// Standard Uses +use std::marker::PhantomData; + +// Local Uses +use crate::schema::ir::context::SchemaContext; +use crate::schema::ir::compiler::report::CompileError; +use crate::utils::codemap::Span; + +// External Uses +use ariadne::Color; + + +#[derive(Debug)] +pub struct ReportDetails { + // pub id: u16, + // pub message: String, + pub(crate) phantom: PhantomData, // pub kind: T, + pub span: Option +} + +#[allow(unused)] +impl ReportDetails { + fn code(&self) -> u8 { + 0 + } +} + +#[allow(unused)] +pub fn report_errors(context: SchemaContext, errors: Vec>) { + let out = Color::Fixed(81); + + /* + errors.into_iter().for_each(|e| { + let span = context. + Report::build(ReportKind::Error, &provenance.0, 12) + .with_code(e.code()).with_message(format!("{:?}", e.kind)) + .with_label( + Label::new((&provenance.0, e.span.unwrap()...e.span.unwrap().end)) + .with_message(format!("This is of type {:?}", e.kind.fg(out))) + .with_color(out), + ) + .finish().print(&mut source) + .unwrap(); + }); + */ +} + + +#[allow(unused)] +pub fn report_warnings(context: SchemaContext, warnings: Vec>) { + todo!() +} + + diff --git a/core/src/schema/idl/ast/mod.rs b/core/src/schema/idl/ast/mod.rs new file mode 100644 index 0000000..8835a22 --- /dev/null +++ b/core/src/schema/idl/ast/mod.rs @@ -0,0 +1,3 @@ +// Relative Modules +pub mod unit; +pub mod old_unit; diff --git a/src/ir/unit.rs b/core/src/schema/idl/ast/old_unit.rs similarity index 81% rename from src/ir/unit.rs rename to core/src/schema/idl/ast/old_unit.rs index 28726e2..4bac37d 100644 --- a/src/ir/unit.rs +++ b/core/src/schema/idl/ast/old_unit.rs @@ -1,11 +1,10 @@ // Standard Uses // Local Uses -use crate::ir::primitive::{Primitive, TypeValue}; // External Uses - +/* /// Intermediate Representation Unit #[allow(unused)] #[derive(Debug, Eq, PartialEq)] @@ -23,20 +22,6 @@ pub enum Unit { } -#[allow(unused)] -#[derive(Debug, Eq, PartialEq)] -pub enum Type { - Primitive(Primitive), - Union, - Namespaced -} - -#[allow(unused)] -#[derive(Debug, Eq, PartialEq)] -pub enum DefaultValue { - Any -} - #[derive(Debug, Eq, PartialEq)] pub struct Settings { pub id: String, @@ -46,7 +31,7 @@ pub struct Settings { #[derive(Debug, Eq, PartialEq)] pub struct Const { pub id: String, - pub type_: TypeValue, + pub type_: Kind, pub default_value: Vec } @@ -69,7 +54,7 @@ pub struct Enum { #[derive(Debug, Eq, PartialEq)] pub struct EnumVariant { pub id: String, - pub type_: Option + pub type_: Option } #[allow(unused)] @@ -93,7 +78,7 @@ pub struct Field { pub index: u8, pub optional: bool, pub id: String, - pub type_: TypeValue, + pub type_: Kind, pub default_value: Vec, } @@ -117,7 +102,7 @@ pub struct Function { pub async_: bool, pub direction: Direction, pub arguments: Vec, - pub return_: Vec, + pub return_: Vec, pub parameters: Vec, pub throws: Vec } @@ -126,11 +111,6 @@ pub struct Function { #[derive(Debug, Eq, PartialEq)] pub struct Argument { pub id: Option, - pub type_: TypeValue + pub type_: Kind } - -#[allow(unused)] -pub fn from_binary(data: Vec) -> Unit { - todo!() -} - +*/ \ No newline at end of file diff --git a/core/src/schema/idl/ast/unit.rs b/core/src/schema/idl/ast/unit.rs new file mode 100644 index 0000000..7be2b39 --- /dev/null +++ b/core/src/schema/idl/ast/unit.rs @@ -0,0 +1,167 @@ +// Standard Uses +use std::rc::Rc; + +// Local Uses +use crate::utils::codemap::{CodeMap, Span}; + +// External Uses + + +pub type OrderIndex = u16; + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum Direction { Client, Server, Both } + + +#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Deserialize, Serialize)] +pub enum ASTUnit { + Namespace(Span, String), + Import(Span, String), + Docstring { + variable: Option, + description: String + }, + Constant { + docstring: Vec, + name: (Span, String), + kind: (Span, String), + default_value: Option<(Span, String)>, + }, + Property { + name: (Span, String), + expression: Option<(Span, String)> + }, + Parameter { + name: (Span, String), + default_value: (Span, String) + }, + ExpressionBlock { + function_calls: Vec + }, + // + Enum { + docstring: Vec, + name: (Span, String), + variants: Vec + }, + EnumVariant { + name: (Span, String), + kind: Option<(Span, String)> + }, + Settings { + docstring: Vec, + name: (Span, String), + parameters: Vec, + }, + Struct { + docstring: Vec, + parameters: Vec, + name: (Span, String), + fields: Vec, + }, + Protocol { + docstring: Vec, + parameters: Vec, + name: (Span, String), + functions: Vec + }, + Function { + docstring: Vec, + parameters: Vec, + name: (Span, String), + asynchronous: Option, + // direction: Direction, + arguments: Vec, + // returns: Vec, + returns: Vec<(Span, String)>, + throws: Vec<(Span, String)> + }, + Argument { + name: Option<(Span, String)>, // TODO: Having optional names might not be a good thing + kind: (Span, String) + }, + Error { + docstring: Vec, + parameters: Vec, + name: (Span, String), + properties: Vec, + fields: Vec + }, + Validator { + docstring: Vec, + properties: Vec, + name: (Span, String), + expression_block: Box + }, + Field { + docstring: Vec, + parameters: Vec, + // index: OrderIndex, + optional: bool, + name: String, + kind: String, + default_value: Option, + } +} + +pub(crate) fn namespace(units: &Vec) -> &String { + let mut namespace: Option<&String> = None; + + for (_, unit) in units { + match unit { + ASTUnit::Namespace(_, n) => namespace = Some(n), + _ => {} + } + } + + namespace.unwrap() +} + +#[derive(PartialEq, Debug)] +pub enum UnitIndex { + Index { + path: String, + source: String, + // nodes: Vec + }, + Node { + index: u32, + start_position: u32, length: u32 + } +} + +pub type SpannedUnit = (Span, ASTUnit); +pub type SourcedWhole = (CodeMap, Vec); +pub type SourcedWholeRc = (CodeMap, Vec>); + + +pub trait Details<'a> { + fn find_namespace(&self) -> Option<&'a SpannedUnit>; +} + +impl<'a> Details<'a> for &'a Vec { + fn find_namespace(&self) -> Option<&'a SpannedUnit> { + for unit in self.iter() { + match &unit.1 { + ASTUnit::Namespace(_, _) => return Some(unit), + _ => {} + } + } + + None + } +} + +impl<'a> Details<'a> for &'a Vec> { + fn find_namespace(&self) -> Option<&'a SpannedUnit> { + for unit in self.iter() { + match &unit.1 { + ASTUnit::Namespace(_, _) => return Some(unit), + _ => {} + } + } + + None + } +} diff --git a/core/src/schema/idl/constants.rs b/core/src/schema/idl/constants.rs new file mode 100644 index 0000000..c263020 --- /dev/null +++ b/core/src/schema/idl/constants.rs @@ -0,0 +1,9 @@ +// Standard Uses + +// Local Uses + +// External Uses + + +pub const SCHEMA_EXTENSION: &str = "ids"; +// pub const UNIT_EXTENSION: &str = "idu"; diff --git a/core/src/schema/idl/idl.pest b/core/src/schema/idl/idl.pest new file mode 100644 index 0000000..065c801 --- /dev/null +++ b/core/src/schema/idl/idl.pest @@ -0,0 +1,261 @@ +// IDL grammar +schema = _{ + COMMENT* ~ MULTILINE_COMMENT* + ~ WS? + ~ namespace + ~ ( + COMMENT | import + | settings | constant + | validator | enumeration + | structure | error | protocol + )* +} + +namespace = { + "namespace" ~ WS ~ domain +} + +import = { + WS ~ "import" ~ WS + ~ domain_namespaced +} + +constant = { + WS ~ "const" ~ WS ~ id + ~ WS? ~ ":" ~ WS? + ~ kind ~ (WS? ~ "=" ~ WS? ~ value)? +} + +settings = { + WS? ~ docstring* + ~ WS? ~ "settings" ~ WS? ~ id? ~ WS? + ~ "{" ~ WS? ~ parameter* ~ WS? ~ "}" +} + +enumeration = { + WS? ~ docstring* ~ property* + ~ "enum" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ enum_variant+ ~ WS? ~ "}" + ~ WS? +} +enum_variant = { + (index ~ "#")? ~ WS? + // TODO: Uncomment and replace the line below when this feature will be addressed + // ~ id ~ enum_variant_field? + ~ id + ~ WS? +} +enum_variant_field = { + "(" + ~ kind + ~ ")" +} + +validator = { + WS? ~ docstring* ~ property* + ~ "validator" ~ WS ~ id ~ WS? + ~ "{" ~ WS? + ~ field* + ~ validator_expr_block + ~ WS? ~ "}" + ~ WS? +} +validator_expr_block = { + WS? ~ "validate" ~ WS?~ "=" + ~ WS? ~ "{" ~ WS? + ~ expression_block + ~ WS? ~ "}" ~ WS? +} + +expression = { + (operation ~ WS? ~ boolean_operator? ~ WS?)+ +} +item = { + domain_namespaced | domain | variable +} +function_call = { + WS? ~ item + ~ WS? ~ "(" ~ WS? + ~ function_call_arg* + ~ WS? ~ ")" ~ WS? +} +function_call_arg = { + WS? ~ ","? ~ WS? + ~ (operation | function_call | value) + ~ WS? +} + +entity = { number | variable } +operation = { + entity ~ WS? + ~ (boolean_operator | operator) + ~ WS? ~ (value | entity)+ +} +operator = { + "==" | "!=" + | "<" | ">" + | "+" | "-" | "/" + | "|" +} +boolean_operator = { + "or" | "and" +} + +structure = { + WS? ~ docstring? + ~ WS? ~ property* + ~ "struct" ~ WS ~ id ~ WS? + ~ "{" ~ WS? + ~ (constant | field)+ + ~ WS? ~ "}" + ~ WS? +} +field = { + WS? ~ property* + // ~ (index ~ "#")? + ~ (WS? ~ requirement)? + ~ WS? ~ id ~ WS? ~ ":" ~ WS? ~ kind + ~ (WS? ~ "=" ~ WS? ~ value)? ~ WS? +} +index = @{ digit } +requirement = { "optional" } + +error = { + WS? ~ docstring? ~ property* + ~ "error" ~ WS ~ id ~ WS? + ~ "{" ~ WS? + ~ (parameter | field)+ + ~ WS? ~ "}" ~ WS? +} + +protocol = { + WS? ~ docstring? ~ property* + ~ "protocol" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ function* ~ WS? ~ "}" +} +function = { + WS? ~ property* + ~ (index ~ WS? ~ "#" ~ WS?)? + ~ (asynchronous ~ WS?)? + ~ (direction ~ WS?)? + ~ "function" ~ WS ~ id ~ WS? + ~ "(" ~ WS? ~ argument* ~ WS? ~ ")" + ~ (WS? ~ "->" ~ WS? ~ returns+)? + // ~ (WS? ~ ":" ~ WS? ~ parameter+)? + ~ (WS? ~ "!" ~ WS? ~ throws)? +} +direction = { "client" | "server" } + +asynchronous = { "async" } +argument = { + ","? ~ WS? + ~ ((id ~ WS? ~ ":" ~ WS? ~ kind) | kind) + ~ WS? +} +returns = { ","? ~ WS? ~ (kind) ~ WS? } +parameter = { + WS? ~ id ~ WS? ~ "=" ~ WS? ~ value ~ WS? +} +throws = { + function_call +} + + +// Common Rules +property = { + WS? ~ "@" + ~ WS? ~ property_domain + ~ WS? ~ "=" ~ WS? + ~ property_expression ~ WS? +} +property_domain = { + variable | domain +} +property_expression = { + (domain_namespaced | domain + | number | property_array) +} +property_array = { + "[" ~ WS? + ~ property_instance* + ~ WS? ~ "]" +} +property_instance = { + WS? ~ domain + ~ "(" ~ property_attribute* ~ ")" + ~ WS? +} +property_attribute = { + WS? ~ id ~ "=" ~ kind +} + +expression_block = { function_call* } + +variable = @{ (id | kind | ".")+ } +domain = @{ (id | "::")+ } +domain_namespaced = @{ (id | "::" | "_")+ } +number = @{ digit+ } +id = @{ (alpha | "_")+ } +kind = @{ (alpha | digit)+ } + +instantiation = { + (domain | domain_namespaced) + ~ "(" ~ domain ~ ")" +} + +docstring = { + "///" ~ + (docstring_property | docstring_description) + ~ NEWLINE +} +docstring_property = { + " "* ~ "@" ~ " "* ~ domain + ~ " "* ~ ":" + ~ " "? ~ docstring_description +} +docstring_description = @{ + (!NEWLINE ~ ANY)+ +} + +value = { + "true" | "false" | number + | string | string_interpolated + | instantiation + | variable + | domain | domain_namespaced +} + +string = { + "\"" ~ string_inner ~ "\"" +} +string_interpolated = { + "f" ~ "\"" ~ string_interpolated_inner ~ "\"" +} +string_interpolated_inner = _{ + (string_interpolation | char)* +} +string_interpolation = _{ "{" ~ domain ~ "}" } + +string_inner = _{ + (string_interpolation | char)* +} +char = { + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) +} + + +alpha = { 'a'..'z' | 'A'..'Z' } +digit = { '0'..'9' } + +WS = _{ (" " | "\t" | "\n")+ } +COMMENT = _{ + !"///" ~ + "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE +} +MULTILINE_COMMENT = _{ + "/*" + ~ (MULTILINE_COMMENT | !"*/" ~ ANY)* + ~ "*/" +} diff --git a/core/src/schema/idl/mod.rs b/core/src/schema/idl/mod.rs new file mode 100644 index 0000000..a34d147 --- /dev/null +++ b/core/src/schema/idl/mod.rs @@ -0,0 +1,5 @@ +// Relative Modules +pub mod constants; +pub mod ast; +pub mod parser; +pub mod parser_new; diff --git a/core/src/schema/idl/parser.rs b/core/src/schema/idl/parser.rs new file mode 100644 index 0000000..9a22b8f --- /dev/null +++ b/core/src/schema/idl/parser.rs @@ -0,0 +1,389 @@ +// Standard Uses +use std::sync::Arc; + +// Local Uses +use crate::schema::idl::ast::unit::{ASTUnit, SpannedUnit}; +use crate::utils::codemap::FileMap; + +// External Uses +use pest::iterators::Pair; + + +#[derive(Parser)] +#[grammar = "schema/idl/idl.pest"] +pub struct IDLParser; + +/* +pub fn parse_inner(pairs: Pair) -> Result { + match pairs.as_rule() { + Rule::namespace => { + Ok(ASTUnit::Namespace(pairs.into_inner().as_str().to_owned())) + }, + Rule::import => { + Ok(ASTUnit::Import(pairs.into_inner().as_str().to_owned())) + } + Rule::constant => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut id: Option = None; + let mut kind: Option = None; + let mut default_value: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => id = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(pair.as_str().to_owned()), + Rule::value => default_value = to_value_other(pair), + missing => panic!("Rule not implemented on 'constant': {:?}", missing) + } + } + + Ok(ASTUnit::Constant { + docstring: docstrings, + name: id.ok_or("Id is not present").unwrap(), + kind: kind.ok_or("Type is not present").unwrap(), + default_value, + }) + }, + Rule::settings => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut name: Option = None; + let mut parameters: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::parameter => parameters.push(to_parameter(pair)), + missing => panic!("Rule not implemented on 'settings': {:?}", missing) + } + } + + Ok(ASTUnit::Settings { + docstring: docstrings, + name: name.unwrap(), + parameters, + }) + }, + Rule::enumeration => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut name: Option = None; + let mut variants: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::enum_variant => { + let mut inner = pair.into_inner(); + let name = inner.next().unwrap().as_str().to_string(); + let kind = inner.next().map(|s| s.as_str().to_string()); + + variants.push(ASTUnit::EnumVariant { + name, kind, + }); + }, + missing => panic!("Rule not implemented on 'enumeration': {:?}", missing) + } + } + + Ok(ASTUnit::Enum { + docstring: docstrings, + name: name.unwrap(), variants, + }) + }, + Rule::structure => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut parameters: Vec = vec![]; + let mut name: Option = None; + let mut fields: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::parameter => parameters.push(to_parameter(pair)), + Rule::field => fields.push(to_field(pair)), + missing => panic!("Rule not implemented on 'structure': {:?}", missing) + } + } + + Ok(ASTUnit::Struct { + docstring: docstrings, parameters, + name: name.unwrap(), fields: vec![], + }) + }, + Rule::validator => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut properties: Vec = vec![]; + let mut name: Option = None; + let mut expression_block: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::property => properties.push(to_property(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::expression_block => + expression_block = Some(to_expression_block(pair)), + missing => panic!("Rule not implemented on 'validator': {:?}", missing) + } + } + + Ok(ASTUnit::Validator { + docstring: docstrings, properties, + name: name.unwrap(), expression_block: Box::from(expression_block.unwrap()), + }) + }, + Rule::protocol => { + let pairs = pairs.into_inner(); + + let mut docstrings = vec![]; + let mut parameters = vec![]; + let mut name: Option = None; + let mut functions = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::property => parameters.push(to_property(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::function => functions.push(to_function(pair)), + missing => panic!("Rule not implemented on 'Protocol': {:?}", missing) + } + } + + Ok(ASTUnit::Protocol { + docstring: docstrings, + parameters, + name: name.unwrap(), + functions, + }) + }, + r => panic!("Rule not implemented: {:?}", r) + } +} +*/ + +pub fn to_docstring(pair: Pair, file: &Arc) -> SpannedUnit { + let pair = pair.into_inner().next().unwrap(); + + match pair.as_rule() { + Rule::docstring_property => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let pairs = pair.into_inner(); + + let mut variable: Option = None; + let mut description: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::domain => variable = Some(pair.as_str().to_owned()), + Rule::docstring_description => description = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented: {:?}", r) + } + } + + return (unit_span, ASTUnit::Docstring { + variable, description: description.unwrap(), + }) + }, + Rule::docstring_description => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + + return (unit_span, ASTUnit::Docstring { + variable: None, description: pair.as_str().to_owned() + }) + }, + r => panic!("Rule not implemented: {:?}", r) + } +} + +/* +pub fn to_parameters(mut pairs: Pairs) -> Vec { + let mut params = vec![]; + + let t = pairs.as_str(); + while let Some(pair) = pairs.next() { + let temp = pair.as_str(); + + params.push(to_parameter(pair.into_inner())); + } + + params +} +*/ + +/* +pub fn to_parameters(mut pairs: Pairs) -> Vec { + let mut params = vec![]; + + while let Some(pair) = pairs.next() { + params.push(to_parameter(pair.into_inner())); + } + + params +} +*/ + +pub fn to_value_other(pair: Pair) -> Option { + let inner = pair.into_inner().next().unwrap(); + + match inner.as_rule() { + Rule::string => Some(inner.as_str().to_string()), + Rule::string_interpolated => Some(inner.as_str().to_string()), + Rule::number => Some(inner.as_str().to_string()), + r => panic!("Rule not implemented in 'value': {:?}", r) + } + + // "".to_string() +} + +#[allow(unused)] +pub fn to_field(pair: Pair, file: &Arc) -> ASTUnit { + let pairs = pair.into_inner(); + + let mut docstring = vec![]; + let mut parameters = vec![]; + let mut optional: bool = false; + let mut name: Option = None; + let mut kind: Option = None; + let mut default_value: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)), + Rule::parameter => {} // parameters.push(to_parameter(pair, file)), + Rule::requirement => optional = true, + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(pair.as_str().to_owned()), + Rule::value => default_value = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented in 'field': {:?}", r) + } + } + + ASTUnit::Field { + docstring, parameters, + optional, name: name.unwrap(), kind: kind.unwrap(), default_value, + } +} + +/* +pub fn to_property(pair: Pair, file: &Arc) -> ASTUnit { + let inner = pair.into_inner().next().unwrap(); + + let mut name: Option = None; + let mut expression: Option = None; + + match inner.as_rule() { + Rule::property_domain => name = Some(inner.as_str().to_string()), + Rule::property_expression => expression = Some(inner.as_str().to_string()), + r => panic!("Rule not implemented in 'property': {:?}", r) + } + + ASTUnit::Property { name: name.unwrap(), expression } +} +*/ + +#[allow(unused)] +fn to_function(pair: Pair) -> ASTUnit { + let inner = pair.into_inner(); + + let mut synchronous = true; + // let mut direction = Direction::Both; + let mut name: Option = None; + let arguments: Vec = vec![]; + // let mut properties = vec![]; + let mut returns = vec![]; + // let throws = vec![]; + + for pair in inner { + match pair.as_rule() { + // Rule::index => index = Some(pair.as_str().parse().unwrap()), + Rule::property => {} // properties.push(to_property(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + /* + Rule::direction => direction = match pair.as_str() { + "client" => Direction::Client, + "server" => Direction::Server, + dir => panic!("Direction {:#} does not exist", dir) + }, + */ + Rule::asynchronous => synchronous = false, + /* + Rule::argument => { + arguments.push(to_argument(pair.into_inner())) + } + */ + Rule::returns => { + returns.push(pair.into_inner().next().unwrap().as_str().to_owned()); + } + Rule::parameter => { + panic!() + } + Rule::throws => { + panic!() + } + r => panic!("Rule not implemented in 'function': {:?}", r) + } + } + + todo!() + /* + ASTUnit::Function { + docstring: vec![], + name: name.unwrap(), + asynchronous, + arguments, + returns, + throws, + } + */ +} + +/* +fn to_argument(pairs: Pairs) -> Argument { + let mut id: Option = None; + let mut kind: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::id => id = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(primitive::to_type(pair.as_str())), + _ => unreachable!() + } + } + + Argument { id, type_: kind.unwrap() } +} +*/ + +pub fn to_expression_block(pair: Pair) -> ASTUnit { + let inner = pair.into_inner().next().unwrap(); + + // let mut expression = vec![]; + let function_calls: Vec = vec![]; + + match inner.as_rule() { + Rule::function_call => { + // expression.push() + }, + r => panic!("Rule not implemented in 'expression_block': {:?}", r) + } + + ASTUnit::ExpressionBlock { + function_calls + } +} diff --git a/core/src/schema/idl/parser_new.rs b/core/src/schema/idl/parser_new.rs new file mode 100644 index 0000000..e9ec72f --- /dev/null +++ b/core/src/schema/idl/parser_new.rs @@ -0,0 +1,478 @@ +// Standard Uses +use std::path::Path; +use std::rc::Rc; +use std::sync::Arc; + +// Local Uses +use crate::utils::codemap::{CodeMap, FileMap}; +use crate::schema::idl::ast::unit::{ASTUnit, SourcedWholeRc, SpannedUnit}; + +// External Uses +use eyre::{bail, Result}; +use pest::iterators::Pair; +use pest::Parser; + + +#[derive(Parser)] +#[grammar = "schema/idl/idl.pest"] +pub struct SchemaParser; + + +pub fn from_path(path: &Path) -> Result { + if !path.exists() { bail!("Path doesn't exist: {:?}", path) } + let source = std::fs::read_to_string(path).unwrap(); + + let sourced_whole = parse_source( + source.clone(), + path.file_name().unwrap().to_str().unwrap().to_owned() + ); + + sourced_whole +} + +pub fn parse_source(source: String, name: String) -> Result { + let mut codemap = CodeMap::new(); + let file = codemap.insert_file(name, source.clone()); + + let pairs = SchemaParser::parse(Rule::schema, source.as_str())?; + let mut units = vec![]; + + for pair in pairs { + if let Ok(unit) = parse_inner(pair, &file) { + units.push(Rc::new(unit)) + } + } + + Ok((codemap, units)) +} + +#[allow(unused)] +pub fn parse_inner(pair: Pair, file: &Arc) -> Result { + match pair.as_rule() { + Rule::namespace => { + let span = pair.as_span(); + let namespace_pair = pair.into_inner().next().unwrap(); + let namespace_span = namespace_pair.as_span(); + let namespace = namespace_pair.as_str().to_owned(); + + Ok(( + file.insert_span(span.start(), span.end()), + ASTUnit::Namespace( + file.insert_span(namespace_span.start(), namespace_span.end()), + namespace + ) + )) + }, + Rule::import => { + let span = pair.as_span(); + let import_pair = pair.into_inner().next().unwrap(); + let import_span = import_pair.as_span(); + let import = import_pair.as_str().to_owned(); + + Ok(( + file.insert_span(span.start(), span.end()), + ASTUnit::Import( + file.insert_span(import_span.start(), import_span.end()), + import + ) + )) + }, + Rule::settings => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let inner = pair.into_inner(); + + let mut docstring = vec![]; + let mut name = None; + let mut parameters = vec![]; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)?), + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + Rule::parameter => parameters.push(to_parameter(pair, file)?), + missing => panic!("Rule not implemented in settings: {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::Settings { + docstring, + name: name.unwrap(), + parameters, + })) + }, + Rule::structure => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let inner = pair.into_inner(); + + let mut docstring = vec![]; + let parameters = vec![]; + let mut name = None; + let mut fields = vec![]; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)?), + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + Rule::field => fields.push(to_field(pair, file)?), + missing => panic!("Rule not implemented in structure: {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::Struct { + docstring, parameters, name: name.unwrap(), fields, + })) + } + Rule::constant => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + + let mut name = None; + let mut kind = None; + let mut default_value = None; + + for pair in pair.into_inner() { + let span = pair.as_span(); + match pair.as_rule() { + Rule::id => { + name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )); + }, + Rule::kind => { + kind = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )) + }, + Rule::value => { + default_value = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )) + }, + missing => panic!("Rule not implemented for constants: '{:?}'", missing) + } + } + + Ok((unit_span, ASTUnit::Constant { + docstring: vec![], + name: name.unwrap(), kind: kind.unwrap(), + default_value + } + )) + }, + Rule::enumeration => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let mut inner = pair.into_inner(); + + let docstring = vec![]; + let mut name = None; + let mut variants = vec![]; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + Rule::enum_variant => variants.push(to_enum_variant(pair, file)?), + missing => panic!("Rule not implemented for enumeration: '{:?}'", missing) + } + } + + Ok((unit_span, ASTUnit::Enum { docstring, name: name.unwrap(), variants, })) + } + Rule::error => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let mut inner = pair.into_inner(); + + let mut docstring = vec![]; + let mut parameters = vec![]; + let mut name = None; + let properties = vec![]; + let mut fields = vec![]; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)?), + Rule::parameter => parameters.push(to_parameter(pair, file)?), + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + Rule::property => parameters.push(to_property(pair, file)?), + Rule::field => fields.push(to_field(pair, file)?), + missing => panic!("Rule not implemented in error: {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::Error { + docstring, parameters, name: name.unwrap(), fields, properties, + })) + } + Rule::protocol => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let mut inner = pair.into_inner(); + + let mut docstring = vec![]; + let mut parameters = vec![]; + let mut name = None; + let mut functions = vec![]; + + for pair in inner { + let span = pair.as_span(); + let next = &file.next_id; + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)?), + Rule::property => parameters.push(to_parameter(pair, file)?), + Rule::id => { + name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )) + }, + Rule::function => functions.push(to_function(pair, file)?), + missing => panic!("Rule not implemented in 'protocol': {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::Protocol { + docstring, parameters, name: name.unwrap(), functions + })) + }, + missing => panic!("Rule not implemented: {:?}", missing) + } +} + + +pub fn to_docstring(pair: Pair, file: &Arc) -> Result { + let pair = pair.into_inner().next().unwrap(); + + match pair.as_rule() { + Rule::docstring_property => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let pairs = pair.into_inner(); + + let mut variable: Option = None; + let mut description: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::domain => variable = Some(pair.as_str().to_owned()), + Rule::docstring_description => description = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented: {:?}", r) + } + } + + return Ok((unit_span, ASTUnit::Docstring { + variable, description: description.unwrap(), + })) + }, + Rule::docstring_description => { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + + return Ok((unit_span, ASTUnit::Docstring { + variable: None, description: pair.as_str().to_owned() + })) + }, + r => panic!("Rule not implemented: {:?}", r) + } +} + + +#[allow(unused)] +pub fn to_parameter(pair: Pair, file: &Arc) -> Result { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let mut inner = pair.into_inner(); + + let name_pair = inner.next().unwrap(); + let name_span = name_pair.as_span(); + let default_value = inner.next().unwrap(); + let default_value_span = default_value.as_span(); + + Ok((unit_span, ASTUnit::Parameter { + name: ( + file.insert_span(name_span.start(), name_span.end()), + name_pair.as_str().to_owned() + ), + default_value: ( + file.insert_span(default_value_span.start(), default_value_span.end()), + default_value.as_str().to_owned() + ) + })) +} + + +#[allow(unused)] +pub fn to_field(pair: Pair, file: &Arc) -> Result { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let mut inner = pair.into_inner(); + + let mut docstring = vec![]; + let mut parameters = vec![]; + let mut optional: bool = false; + let mut name: Option = None; + let mut kind: Option = None; + let mut default_value: Option = None; + + for pair in inner { + match pair.as_rule() { + Rule::docstring => docstring.push(to_docstring(pair, file)?), + Rule::parameter => {} // parameters.push(to_parameter(pair, file)), + Rule::property => {} // + Rule::requirement => optional = true, + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(pair.as_str().to_owned()), + Rule::value => default_value = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented in 'field': {:?}", r) + } + } + + Ok((unit_span, ASTUnit::Field { + docstring, parameters, + optional, name: name.unwrap(), kind: kind.unwrap(), default_value, + })) +} + +pub fn to_property(pair: Pair, file: &Arc) -> Result { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let inner = pair.into_inner().next().unwrap(); + + let mut name = None; + let mut expression = None; + + let span = inner.as_span(); + match inner.as_rule() { + Rule::property_domain => name = Some(( + file.insert_span(span.start(), span.end()), + inner.as_str().to_string() + )), + Rule::property_expression => expression = Some(( + file.insert_span(span.start(), span.end()), + inner.as_str().to_string() + )), + missing => panic!("Rule not implemented in 'property': {:?}", missing) + } + + Ok((unit_span, ASTUnit::Property { name: name.unwrap(), expression })) +} + + +#[allow(unused)] +fn to_enum_variant(pair: Pair, file: &Arc) -> Result { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let inner = pair.into_inner(); + + let mut name = None; + let kind = None; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + missing => panic!("Rule not implemented for enum_variant: {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::EnumVariant { + name: name.unwrap(), kind, + })) +} + +#[allow(unused)] +fn to_function(pair: Pair, file: &Arc) -> Result { + let pair_span = pair.as_span(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let inner = pair.into_inner(); + + let mut name = None; + let asynchronous = None; + let docstring = vec![]; + let mut parameters = vec![]; + let mut arguments = vec![]; + let mut returns = vec![]; + let mut throws = vec![]; + + for pair in inner { + let span = pair.as_span(); + match pair.as_rule() { + Rule::property => parameters.push(to_parameter(pair, file)?), + Rule::id => name = Some(( + file.insert_span(span.start(), span.end()), + pair.as_str().to_owned() + )), + Rule::argument => { + let pair_span = pair.as_span(); + let mut inner = pair.into_inner(); + let unit_span = file.insert_span(pair_span.start(), pair_span.end()); + let name_pair = inner.next().unwrap(); + let name_span = name_pair.as_span(); + let kind_pair = inner.next().unwrap(); + let kind_span = kind_pair.as_span(); + + arguments.push((unit_span, ASTUnit::Argument { + name: Some(( + file.insert_span(name_span.start(), name_span.end()), + name_pair.as_str().to_owned() + )), + kind: ( + file.insert_span(kind_span.start(), kind_span.end()), + kind_pair.as_str().to_owned() + ), + })) + }, + Rule::returns => { + let mut inner = pair.into_inner(); + let name_pair = inner.next().unwrap(); + let name_span = name_pair.as_span(); + + returns.push(( + file.insert_span(name_span.start(), name_span.end()), + name_pair.as_str().to_owned() + )); + }, + Rule::throws => { + let mut inner = pair.into_inner(); + let name_pair = inner.next().unwrap(); + let name_span = name_pair.as_span(); + + throws.push(( + file.insert_span(name_span.start(), name_span.end()), + name_pair.as_str().to_owned() + )); + } + missing => panic!("Rule not implemented for function: {:?}", missing) + } + } + + Ok((unit_span, ASTUnit::Function { + docstring, parameters, name: name.unwrap(), asynchronous, + arguments, returns, throws + })) +} + diff --git a/core/src/schema/ir/compiler/interpreted/interpreter.rs b/core/src/schema/ir/compiler/interpreted/interpreter.rs new file mode 100644 index 0000000..bf8240c --- /dev/null +++ b/core/src/schema/ir/compiler/interpreted/interpreter.rs @@ -0,0 +1,301 @@ +// Standard Uses +use std::cell::{RefCell, RefMut}; +use std::rc::Rc; + +// Local Uses +use crate::schema::idl::ast::unit::{ASTUnit, SpannedUnit}; +use crate::schema::ir::context::SchemaContext; +use crate::schema::ir::compiler::interpreted::primitive::KindValue; +use crate::schema::ir::compiler::report::CompileError; +use crate::schema::ir::frozen::unit::FrozenUnit; +use crate::project::ir::context::ProjectContext; +use crate::report::ReportDetails; + +// External Uses + + + +#[allow(unused)] +pub fn interpret_context( + schema_context: &Rc>, project_context: &RefMut +) -> Result<(), ReportDetails> { + let mut interpreted: Vec = vec![]; + + let mut context = Some(schema_context.borrow()); + for i in 0..context.as_ref().unwrap().schema.1.len() { + let spanned_unit = &context.unwrap().schema.1[i]; + + use crate::schema::idl::ast::unit::ASTUnit::*; + match &spanned_unit.1 { + Namespace(s, n) => { + let name = n.clone(); + let Some(ctx) + = project_context.find_schema_by_import(&n) else + { + return Err(ReportDetails { + // kind: "Namespace".to_string(), + /* + message: format!( + "Namespace {} used in another schema: {} and {},\n \ + only one schema per namespace is allowed", + n, context.main.0.filename(), unit_namespace.unwrap().0.filename() + ), + start: 0, end: 0, + */ + phantom: Default::default(), + span: None, + }) + }; + + context = None; + let mut ctx_mut = schema_context.borrow_mut(); + + if let Some(name) = &ctx_mut.compile_state.namespace { + panic!("Namespace {} was already previously set", name) + } else { + ctx_mut.compile_state.namespace = Some(name) + } + } + Docstring {..} => { + todo!() + } + Import(s, i) => { + /* + let Some(ctx) + = project_context.find_schema_by_import(i) else { + panic!("No import") + }; + */ + } + // Docstring { .. } => {} + Constant { + docstring, name, + kind, default_value + } => { + /* + let kind_value = primitive::to_kind_value( + schema_context, kind, default_value + ); + */ + } + Property { .. } => {} + Parameter { .. } => {} + ExpressionBlock { .. } => {} + Enum { .. } => {} + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Argument { .. } => {} + Error { .. } => {} + Validator { .. } => {} + Field { .. } => {} + } + + context = Some(schema_context.borrow()); + } + + // Ok(context.unwrap().compile_state.to_frozen()) + Ok(()) + + /* + for (unit, span) in &context.config.1 { + use crate::schema::idl::ast::unit::ASTUnit::*; + match unit { + ASTUnit::Namespace(n) => { + let unit_namespace = context.find_whole_unit_by_import(n); + + /* + if unit_namespace.is_some() { + return Err(ReportDetails { + kind: "Namespace".to_string(), + message: format!( + "Namespace {} used in another schema: {} and {},\n \ + only one schema per namespace is allowed", + n, context.main.0.filename(), unit_namespace.unwrap().0.filename() + ), + start: 0, end: 0, + }) + } + */ + + interpreted.push(FrozenUnit::Namespace(n.clone())); + }, + Import(_) => { + let import = interpret_node(&context, unit)?; + interpreted.push(import); + } + Constant { .. } => { + let constant = interpret_node(&context, unit)?; + interpreted.push(constant); + } + Enum { .. } => { + let r#enum = interpret_node(&context, unit)?; + interpreted.push(r#enum); + } + /* + Unit::Settings { .. } => {} + Unit::Struct { .. } => {} + Unit::Protocol { .. } => {} + Unit::Error { .. } => {} + */ + + Validator { .. } => { + let validator = interpret_node(&context, unit)?; + interpreted.push(validator); + } + /* + //r => panic!("Left to impl: {:?}", r) + */ + _ => {} + } + } + */ +} + +#[allow(unused)] +pub fn interpret_node(context: &SchemaContext, node: &ASTUnit) + -> Result> +{ + let schema_ctx = context.project_context.clone().unwrap(); + let project_ctx = context.project_context.clone().unwrap(); + + use crate::schema::idl::ast::unit::ASTUnit::*; + match node { + Namespace(_, n) => { + let mut found: Option<&SchemaContext> = None; + + /* + for relative_ctx in schema_ctx.borrow().schema_contexts.iter() { + if unit::namespace(&relative_ctx.schema.1) == n { + /* + if found.is_some() { + return Err(ReportDetails { + kind: "namespace".to_string(), + message: format!( + "Found namespace {} when its already declared in {}", + &n, &relative_ctx.main.0.filename() + ), + start: 0, end: 0, + }) + } + */ + + found = Some(relative_ctx) + } + } + */ + } + Import(_, i) => { + let relative_unit = project_ctx.borrow().find_schema_by_import(&i); + + /* + if relative_unit.is_none() { + let relative_unit = relative_unit.unwrap(); + + return Err(ReportDetails { + kind: "import".to_string(), + message: format!("Could not find namespace of {}", relative_unit.0.filename()), + start: 0, end: 0, + }) + } + */ + + return Ok(FrozenUnit::Import(i.clone())) + }, + Constant { + name: (_, name), kind: (_, kind), + default_value, .. + } => { + /* + let kind_value = primitive::to_kind_value(kind, default_value); + + return Ok(FrozenUnit::Constant { + docstring: None, + name: name.clone(), kind_value + }) + */ + } + Enum { name, variants, .. } => { + let mut frozen_variants: Vec = vec![]; + + for variant in variants { + pub(crate) fn to_variant(variant: &ASTUnit) -> KindValue { + match variant { + EnumVariant { name, kind } => { + if kind.is_none() { + return KindValue::EnumVariant( + name.1.clone(),None + ) + } + + return KindValue::EnumVariant( + name.1.clone(), None + ) + }, + _ => panic!("Should not be here") + } + } + + frozen_variants.push(FrozenUnit::EnumVariant( + to_variant(&variant.1) + )); + } + + return Ok(FrozenUnit::Enum { + docstring: None, + name: name.1.clone(), + variants: frozen_variants + }) + } + /* + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Error { .. } => {} + */ + #[allow(unused)] + Validator { + docstring, properties, + name, expression_block + } => { + let properties = to_properties(properties); + let expr_block = Box::new(FrozenUnit::ExpressionBlock { function_calls: vec![] }); + + + return Ok(FrozenUnit::Validator { + docstring: Some("".to_owned()), // TODO: Docstrings should likely be a vector of units + properties: vec![], + name: name.1.clone(), + expression_block: expr_block + }) + } + /* + Field { .. } => {} + Parameter { .. } => {} + Property { .. } => {} + ExpressionBlock { .. } => {} + */ + missing => unimplemented!("Missing implementation for node '{:?}'", missing) + } + + panic!() +} + +#[allow(unused)] +fn to_properties(nodes: &Vec) -> Vec { + let properties = vec![]; + + for node in nodes { + + } + + properties +} + +pub fn into_frozen_unit() -> FrozenUnit { + todo!() +} diff --git a/core/src/schema/ir/compiler/interpreted/mod.rs b/core/src/schema/ir/compiler/interpreted/mod.rs new file mode 100644 index 0000000..cf71720 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreted/mod.rs @@ -0,0 +1,6 @@ +// Relative Modules +pub mod sanitizer; +pub mod serialization; +pub mod primitive; +pub mod interpreter; + diff --git a/core/src/schema/ir/compiler/interpreted/primitive.rs b/core/src/schema/ir/compiler/interpreted/primitive.rs new file mode 100644 index 0000000..8354c8d --- /dev/null +++ b/core/src/schema/ir/compiler/interpreted/primitive.rs @@ -0,0 +1,186 @@ +// Standard Uses + +// Local Uses + +use std::cell::RefCell; +use std::rc::Rc; +// External Uses +use strum_macros::EnumProperty; +use crate::schema::ir::context::SchemaContext; +use crate::utils::codemap::Span; + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(EnumProperty)] +#[repr(u8)] +pub enum Primitive { + #[strum(props(Name="u8", Description="unsigned 1 byte, 8 bits"))] + U8(Option) = 0, + + #[strum(props(Name="u16", Description="unsigned 2 bytes, 16 bits"))] + U16(Option), + + #[strum(props(Name="u32", Description="unsigned 4 bytes, 32 bits"))] + U32(Option), + + #[strum(props(Name="u64", Description="unsigned 8 bytes, 64 bits"))] + U64(Option), + + #[strum(props(Name="u128", Description="unsigned 16 bytes, 128 bits"))] + U128(Option), + + #[strum(props(Name="", Description=""))] + S8(Option), + + #[strum(props(Name="", Description=""))] + S16(Option), + + #[strum(props(Name="", Description=""))] + S32(Option), S64(Option), + + #[strum(props(Name="", Description=""))] + S128(Option), + + // Float(f32), Double(f64), + + #[strum(props(Name="", Description=""))] + String(Option), + + #[strum(props(Name="", Description=""))] + Namespaced(Option) +} + +impl Primitive { + pub fn value_str(&self) -> Option { + match self { + Primitive::U8(u) => { + if let Some(u) = u { Some(u.to_string()) } else { None } + } + Primitive::U16(u) => { + if let Some(u) = u { Some(u.to_string()) } else { None } + } + Primitive::U32(u) => { + if let Some(u) = u { Some(u.to_string()) } else { None } + } + Primitive::U64(u) => { + if let Some(u) = u { Some(u.to_string()) } else { None } + } + Primitive::U128(u) => { + if let Some(u) = u { Some(u.to_string()) } else { None } + } + Primitive::S8(s) => { + if let Some(u) = s { Some(u.to_string()) } else { None } + } + Primitive::S16(s) => { + if let Some(u) = s { Some(u.to_string()) } else { None } + } + Primitive::S32(s) => { + if let Some(u) = s { Some(u.to_string()) } else { None } + } + Primitive::S64(s) => { + if let Some(u) = s { Some(u.to_string()) } else { None } + } + Primitive::S128(s) => { + if let Some(u) = s { Some(u.to_string()) } else { None } + } + Primitive::String(s) => { + if let Some(s) = s { Some(s.clone()) } else { None } + }, + Primitive::Namespaced(n) => { + if let Some(n) = n { Some(n.clone()) } else { None } + }, + } + } + + pub fn name_description(&self) -> String { + use strum::EnumProperty; + format!("{}({})", self.get_str("Name").unwrap(), self.get_str("Description").unwrap()) + } + + /* + fn kind_value_description(&self) -> (Discriminant, String) { + let id = std::mem::discriminant(self); + let value = self.value_str(); + + (id.into(), value.unwrap()) + } + */ +} + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum KindValue { + Primitive(Primitive), + EnumVariant(String, Option>), + Union(Vec), + Namespaced(String) +} + +impl KindValue { + pub fn name_and_value(&self) -> (String, String) { + match self { + KindValue::Primitive(primitive) => { + return (primitive.name_description(), primitive.value_str().unwrap()) + } + KindValue::EnumVariant(name, value) => { + let value = value.as_ref().unwrap(); + let (variant_name, value) = value.name_and_value(); + return (format!("Enum variant {} ({})'", name, variant_name), value) + } + KindValue::Union(_) => { + todo!() + } + #[allow(unused)] + KindValue::Namespaced(namespace) => { + todo!() + } + } + } +} + + +pub(crate) fn to_kind_value( + schema_context: &Rc>, + kind: &(Span, String), value: &Option<(Span, String)> +) -> KindValue { + if value.is_none() { + return to_kind_only(kind) + } + + KindValue::Primitive( + to_primitive(schema_context, kind, value.clone().unwrap().1).unwrap() + ) +} + +fn to_kind_only(kind: &(Span, String)) -> KindValue { + match kind { + &_ => panic!("Cant") + } +} + +fn to_primitive( + schema_context: &Rc>, + kind: &(Span, String), value: String +) -> Option { + use self::Primitive::*; + let kv = match &*kind.1 { + "u8" => U8(Some(value.parse().unwrap())), + "u16" => U16(Some(value.parse().unwrap())), + "u32" => U32(Some(value.parse().unwrap())), + "u64" => U64(Some(value.parse().unwrap())), + "u128" => U128(Some(value.parse().unwrap())), + + "s8" => S8(Some(value.parse().unwrap())), + "s16" => S16(Some(value.parse().unwrap())), + "s32" => S32(Some(value.parse().unwrap())), + "s64" => S64(Some(value.parse().unwrap())), + "s128" => S128(Some(value.parse().unwrap())), + + "str" => String(Some(value.to_owned())), + _ => { panic!("Got unknown type '{:#}'", kind.1) } + }; + + Some(kv) +} + + diff --git a/core/src/schema/ir/compiler/interpreted/sanitizer.rs b/core/src/schema/ir/compiler/interpreted/sanitizer.rs new file mode 100644 index 0000000..d5901b7 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreted/sanitizer.rs @@ -0,0 +1,12 @@ +// Standard Uses + +// Local Uses +use crate::schema::ir::context::SchemaContext; + +// External Uses + + +#[allow(unused)] +fn sanitize_context_units(context: &SchemaContext) { + todo!() +} diff --git a/core/src/schema/ir/compiler/interpreted/serialization/messagepack.rs b/core/src/schema/ir/compiler/interpreted/serialization/messagepack.rs new file mode 100644 index 0000000..f0f63a7 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreted/serialization/messagepack.rs @@ -0,0 +1,20 @@ +// Standard Uses + +// Local Uses +use crate::schema::idl::ast::unit::ASTUnit; + +// External Uses +#[allow(unused)] +use rmp; + + +// https://docs.rs/rmp/latest/rmp/ +#[allow(unused)] +fn from_bytes(raw: Vec) -> ASTUnit { + todo!() +} + +#[allow(unused)] +fn to_bytes(unit: ASTUnit) -> Vec { + todo!() +} diff --git a/src/ir/serialization.rs b/core/src/schema/ir/compiler/interpreted/serialization/mod.rs similarity index 100% rename from src/ir/serialization.rs rename to core/src/schema/ir/compiler/interpreted/serialization/mod.rs diff --git a/core/src/schema/ir/compiler/interpreter/incremental.rs b/core/src/schema/ir/compiler/interpreter/incremental.rs new file mode 100644 index 0000000..e85038b --- /dev/null +++ b/core/src/schema/ir/compiler/interpreter/incremental.rs @@ -0,0 +1,173 @@ +// Standard Uses + +// Local Uses +use crate::schema::idl::ast::unit; +use crate::schema::idl::ast::unit::ASTUnit; +use crate::schema::ir::compiler::Compile; +use crate::schema::ir::compiler::interpreted::frozen_unit::FrozenUnit; +use crate::schema::ir::compiler::interpreted::primitive; +use crate::schema::ir::compiler::interpreted::primitive::KindValue; +use crate::schema::ir::compiler::interpreted::report::ReportDetails; +use crate::schema::ir::context::Context; + +// External Uses + + +#[allow(unused)] +pub struct IncrementalInterpreter { + context: Context +} + +#[allow(unused)] +impl Compile for IncrementalInterpreter { + type Output = (); + + fn from_ast(ast: Vec) -> Self::Output { + todo!() + } +} + +#[allow(unused)] +impl IncrementalInterpreter { + pub fn interpret_unit(&self) -> Result, ReportDetails> { + let mut interpreted: Vec = vec![]; + + for unit in &self.context.main.1 { + use crate::schema::idl::ast::unit::ASTUnit::*; + match unit { + Namespace(n) => { + // let namespace = n; + interpreted.push(FrozenUnit::Namespace(n.clone())); + }, + Import(_) => { + let import = self.interpret_node( unit)?; + interpreted.push(import); + } + Constant { .. } => { + let constant = self.interpret_node(unit)?; + interpreted.push(constant); + } + Enum { .. } => { + let r#enum = self.interpret_node( unit)?; + interpreted.push(r#enum); + } + /* + Unit::Settings { .. } => {} + Unit::Struct { .. } => {} + Unit::Protocol { .. } => {} + Unit::Error { .. } => {} + Unit::Validator { .. } => {} + */ + //r => panic!("Left to impl: {:?}", r) + _ => {} + } + } + + + Ok(interpreted) + } + + pub fn interpret_node(&self, node: &ASTUnit) -> Result { + use crate::schema::idl::ast::unit::ASTUnit::*; + match node { + Tag(_) => { + + } + Namespace(n) => { + let mut found: Option<&Context> = None; + + for relative_ctx in &self.context.relative_contexts { + if unit::namespace(&relative_ctx.main.1) == n { + if found.is_some() { + return Err(ReportDetails { + kind: "namespace".to_string(), + message: format!( + "Found namespace {} when its already declared in {}", + &n, &relative_ctx.main.0.filename() + ), + start: 0, end: 0, + }) + } + + found = Some(relative_ctx) + } + } + } + Import(i) => { + let relative_unit = self.context.find_whole_unit_by_import(&i); + + if relative_unit.is_none() { + let relative_unit = relative_unit.unwrap(); + + return Err(ReportDetails { + kind: "import".to_string(), + message: format!("Could not find namespace of {}", relative_unit.0.filename()), + start: 0, end: 0, + }) + } + + return Ok(FrozenUnit::Import(i.clone())) + }, + Constant { name, kind, default_value, .. } => { + let kind_value = primitive::to_kind_value(kind, default_value); + + return Ok(FrozenUnit::Constant { + docstring: None, + name: name.clone(), kind_value + }) + } + Enum { name, variants, .. } => { + let mut frozen_variants: Vec = vec![]; + + for variant in variants { + pub(crate) fn to_variant(variant: &ASTUnit) -> KindValue { + match variant { + EnumVariant { name, kind } => { + if kind.is_none() { + return KindValue::EnumVariant( + name.clone(),None + ) + } + + return KindValue::EnumVariant( + name.clone(), None + ) + }, + _ => panic!("Should not be here") + } + } + + frozen_variants.push(FrozenUnit::EnumVariant( + to_variant(variant, ) + )); + } + + return Ok(FrozenUnit::Enum { + docstring: None, + name: name.clone(), variants: frozen_variants + }) + } + /* + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Error { .. } => {} + Validator { .. } => {} + Field { .. } => {} + Parameter { .. } => {} + Property { .. } => {} + ExpressionBlock { .. } => {} + */ + _ => {} + } + + panic!() + } + +} + +pub fn into_frozen_unit() -> FrozenUnit { + todo!() +} diff --git a/core/src/schema/ir/compiler/interpreter/meta_stage.rs b/core/src/schema/ir/compiler/interpreter/meta_stage.rs new file mode 100644 index 0000000..125bb94 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreter/meta_stage.rs @@ -0,0 +1,100 @@ +// Standard Uses +use std::cell::{RefCell, RefMut}; +use std::rc::Rc; + +// Crate Uses +use crate::report::ReportDetails; +use crate::schema::ir::compiler::report::CompileError; +use crate::schema::ir::context::SchemaContext; +use crate::project::ir::context::ProjectContext; +use crate::schema::ir::compiler::interpreter::semi_frozen::SemiFrozenUnit; + +// External Uses + + +pub fn compile_schema_metadata( + schema_context: &Rc>, project_context: &RefMut +) -> Result<(), ReportDetails> { + let mut context = Some(RefCell::borrow(&schema_context)); + + for i in 0..context.as_ref().unwrap().schema.1.len() { + let spanned_unit = &context.as_ref().unwrap().schema.1[i]; + + use crate::schema::idl::ast::unit::ASTUnit::*; + match &spanned_unit.1 { + Namespace(_, n) => { + let name = n.clone(); + let Some(_) + = project_context.find_schema_by_import(&n) else + { + return Err(ReportDetails { + // kind: "Namespace".to_string(), + /* + message: format!( + "Namespace {} used in another schema: {} and {},\n \ + only one schema per namespace is allowed", + n, context.main.0.filename(), unit_namespace.unwrap().0.filename() + ), + start: 0, end: 0, + */ + phantom: Default::default(), + span: None, + }) + }; + + context = None; + let mut ctx_mut = schema_context.borrow_mut(); + + if let Some(name) = &ctx_mut.compile_state.namespace { + panic!("Namespace {} was already previously set", name) + } else { + ctx_mut.compile_state.namespace = Some(name) + } + drop(ctx_mut); + } + Docstring {..} => { + todo!() + } + Import(_, _) => { + /* + let Some(ctx) + = project_context.find_schema_by_import(i) else { + panic!("No import") + }; + */ + } + // Docstring { .. } => {} + Constant { name, ..} => { + let name = name.clone(); + + context = None; + + let mut ctx_mut = schema_context.borrow_mut(); + let spanned_unit = &schema_context.borrow().schema.1[i]; + let su = Rc::clone(&spanned_unit); + ctx_mut.compile_state.consts.entry(su).or_insert( + SemiFrozenUnit::Constant { name: name.clone() } + ); + + context = Some(RefCell::borrow(&schema_context)); + } + Property { .. } => {} + Parameter { .. } => {} + ExpressionBlock { .. } => {} + Enum { .. } => {} + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Argument { .. } => {} + Error { .. } => {} + Validator { .. } => {} + Field { .. } => {} + } + + context = Some(RefCell::borrow(&schema_context)); + } + + Ok(()) +} \ No newline at end of file diff --git a/core/src/schema/ir/compiler/interpreter/mod.rs b/core/src/schema/ir/compiler/interpreter/mod.rs new file mode 100644 index 0000000..678c949 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreter/mod.rs @@ -0,0 +1,4 @@ +// Relative Modules +pub mod meta_stage; +pub mod object_stage; +pub mod semi_frozen; diff --git a/core/src/schema/ir/compiler/interpreter/object_stage.rs b/core/src/schema/ir/compiler/interpreter/object_stage.rs new file mode 100644 index 0000000..e3c757f --- /dev/null +++ b/core/src/schema/ir/compiler/interpreter/object_stage.rs @@ -0,0 +1,17 @@ +// Standard Uses +use std::rc::Rc; +use std::cell::RefCell; + +// Crate Uses +use crate::report::ReportDetails; +use crate::schema::ir::compiler::report::CompileError; +use crate::schema::ir::context::SchemaContext; + +// External Uses + + +pub fn compile_schema_metadata(schema_context: &Rc> +) -> Result<(), ReportDetails> +{ + todo!() +} \ No newline at end of file diff --git a/core/src/schema/ir/compiler/interpreter/semi_frozen.rs b/core/src/schema/ir/compiler/interpreter/semi_frozen.rs new file mode 100644 index 0000000..1b89881 --- /dev/null +++ b/core/src/schema/ir/compiler/interpreter/semi_frozen.rs @@ -0,0 +1,14 @@ +// Standard Uses + +// Crate Uses +use crate::utils::codemap::Span; + +// External Uses + + +pub enum SemiFrozenUnit { + Constant { + name: (Span, String) + }, +} + diff --git a/core/src/schema/ir/compiler/mod.rs b/core/src/schema/ir/compiler/mod.rs new file mode 100644 index 0000000..29db461 --- /dev/null +++ b/core/src/schema/ir/compiler/mod.rs @@ -0,0 +1,33 @@ +// Relative Modules +pub mod interpreter; +pub mod interpreted; +pub mod report; + +// Standard Uses + +// Local Uses +use crate::schema::idl::parser_new; +use crate::schema::idl::ast::unit::{ASTUnit, SourcedWholeRc}; + +// External Uses + + + +pub trait Compile { + type Output; + + fn from_ast(ast: Vec) -> Self::Output; + + fn from_source(source: &str) -> Self::Output { + println!("Compiling source: {}", source); + + let sourced = parser_new::parse_source( + source.to_owned(), "TODO".to_owned() // TODO: We need the source name here + ).unwrap(); + + Self::from_sourced_whole(sourced) + } + + + fn from_sourced_whole(sourced: SourcedWholeRc) -> Self::Output; +} diff --git a/core/src/schema/ir/compiler/report.rs b/core/src/schema/ir/compiler/report.rs new file mode 100644 index 0000000..b506ae6 --- /dev/null +++ b/core/src/schema/ir/compiler/report.rs @@ -0,0 +1,47 @@ +// Standard Uses +use std::fmt::{Display, Formatter}; + +// Crate Uses +use crate::report::ReportDetails; + +// External Uses +use snafu::Snafu; +use strum::EnumProperty; +use strum_macros::FromRepr; + + +// TODO: repr(u16) is here so we could turn a enum variant instance into its corresponding number +// however i didn't find how to do it without having to match pattern every each one of +// the variants and return the number by casting the variant, so strum props were used. +// If in the future we know of a better solution, it should be applied +#[repr(u16)] +#[derive(EnumProperty)] +#[derive(Debug, Snafu)] +pub enum CompileError { + #[strum(props(Id="1"))] + #[snafu(display("Found namespace '{target}' when its already declared in '{origin}'"))] + Namespace { source: std::io::Error, origin: String, target: String } = 1, +} + +impl CompileError { + pub fn code(&self) -> u16 { + self.get_str("Id").expect("Variant does not have a 'Id' property").parse().unwrap() + } +} + +impl Display for ReportDetails { + #[allow(unused)] + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "TODO: Display needs implementation here") + } +} + + +// TODO: Same case as the enum `CompileError` about the repr +#[derive(EnumProperty, FromRepr)] +#[derive(Debug, Snafu)] +pub enum CompileWarning { + #[strum(props(Id="1"))] + #[snafu(display("No dependencies were assigned, this will automatically add Comline's langlib'"))] + DependenciesNotFilled +} diff --git a/core/src/schema/ir/context.rs b/core/src/schema/ir/context.rs new file mode 100644 index 0000000..62f7d66 --- /dev/null +++ b/core/src/schema/ir/context.rs @@ -0,0 +1,73 @@ +// Standard Uses +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::HashMap; + +// Local Uses +use crate::project::ir::context::ProjectContext; +use crate::schema::idl::ast::unit::{SourcedWholeRc, SpannedUnit}; +use crate::schema::ir::compiler::interpreter::semi_frozen::SemiFrozenUnit; +use crate::schema::ir::frozen::unit::FrozenUnit; + +// External Uses + + +#[derive(Default)] +pub struct CompileState { + pub namespace: Option, + pub consts: HashMap, SemiFrozenUnit> +} + +impl CompileState { + pub(crate) fn to_frozen(&self) -> Vec { + let mut interpreted = vec![]; + + interpreted.push(FrozenUnit::Namespace(self.namespace.clone().unwrap())); + + return interpreted + } +} + +pub struct SchemaContext { + pub schema: SourcedWholeRc, + pub frozen_schema: Option>, + pub project_context: Option>>, + pub compile_state: CompileState +} + +#[allow(unused)] +impl SchemaContext { + pub fn with_main(schema: SourcedWholeRc) -> Self{ + Self { + schema, + frozen_schema: None, + project_context: None, + compile_state: Default::default(), + } + } + + pub fn with_project_and_main( + schema: SourcedWholeRc, project: Rc> + ) -> Self { + Self { + schema, + frozen_schema: None, + project_context: Some(project), + compile_state: Default::default(), + } + } + + pub fn with_main_no_std(schema: SourcedWholeRc) -> Self{ + Self { + schema, + frozen_schema: None, + project_context: None, + compile_state: Default::default(), + } + } + + pub(crate) fn sanitize_units(self) { + todo!() + } +} + diff --git a/core/src/schema/ir/diff/mod.rs b/core/src/schema/ir/diff/mod.rs new file mode 100644 index 0000000..9b6c21e --- /dev/null +++ b/core/src/schema/ir/diff/mod.rs @@ -0,0 +1,118 @@ +// Standard Uses +use std::fmt::Debug; + +// Crate Uses +use crate::schema::ir::frozen::unit::{FrozenUnit, FrozenWhole}; +use crate::schema::ir::compiler::interpreted::primitive::KindValue; + +// External Uses +use downcast_rs::{Downcast, impl_downcast}; + + +#[allow(unused)] +pub trait Differ: Downcast + Debug { + fn on_namespace_changed(&mut self, old: &str, new: &str); + fn on_const_name_changed(&mut self, old: &str, new: &str); + fn on_const_kind_changed(&mut self, old: u8, new: u8); + fn on_const_default_value_changed( + &mut self, name: &str, left_kind_value: &KindValue, right_kind_value: &KindValue + ); + + fn differ( + &self, previous: &Vec, next: &FrozenWhole, + document_gen: bool, auto_version: bool + ) { + todo!() + } +} +impl_downcast!(Differ); + + +#[allow(unused)] +pub fn match_differ( + mut listeners: &mut Vec>, + previous: &Vec, next: &Vec +) { + for left in previous { + match left { + FrozenUnit::Namespace(..) => { + search_other(left, next).map(|r| { + let FrozenUnit::Namespace(left) = left else { panic!() }; + let FrozenUnit::Namespace(right) = r else { panic!() }; + + if left != right { + listeners.iter_mut().for_each(|mut l| l.on_namespace_changed( + left, right + )) + } + }); + } + FrozenUnit::Import(..) => { + todo!() + } + FrozenUnit::Constant { + docstring: l_docstring, name: l_name, kind_value: l_kind_value + } => { + search_other(left, next).map(|right| { + let FrozenUnit::Constant { + docstring, name, kind_value + } = right else { panic!() }; + + if l_name != name { + listeners.iter_mut().for_each(|mut differ| { + differ.on_const_name_changed(l_name, name) + }); + } + + if l_kind_value != kind_value { + listeners.iter_mut().for_each(|mut differ| { + differ.on_const_default_value_changed( + name,l_kind_value, kind_value + ) + }); + } + }); + } + FrozenUnit::Struct{..} => { + search_other(left, next).map(|l| { + let FrozenUnit::Struct{..} = left else { panic!() }; + let FrozenUnit::Struct{..} = l else { panic!() }; + + if left != l { + /* + listeners.iter_mut().for_each(|mut l| + l.on_structure_changed(node, l) + ) + */ + } + }); + } + missing => panic!("Node not implemented: {:?}", missing) + } + } +} + + +pub fn search_other<'a>(node: &FrozenUnit, others: &'a Vec) + -> Option<&'a FrozenUnit> +{ + for other in others { + match node { + FrozenUnit::Namespace(..) => { + if let FrozenUnit::Namespace(..) = other { return Some(other) }; + } + FrozenUnit::Import(..) => { + if let FrozenUnit::Import(..) = other { return Some(other) }; + } + FrozenUnit::Constant{..} => { + if let FrozenUnit::Constant{..} = other { return Some(other) }; + } + FrozenUnit::Struct{..} => { + if let FrozenUnit::Struct{..} = other { return Some(other) }; + } + missing => { panic!("Node not implemented: {:?}", missing) } + } + } + + None +} diff --git a/core/src/schema/ir/frozen/blob.rs b/core/src/schema/ir/frozen/blob.rs new file mode 100644 index 0000000..82b3bbb --- /dev/null +++ b/core/src/schema/ir/frozen/blob.rs @@ -0,0 +1,62 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use lz4_flex::{compress_prepend_size, decompress_size_prepended}; +use eyre::{bail, Result}; + + +#[derive(Debug, PartialEq)] +pub enum FrozenBlob { + Content(String) +} + +pub fn to_processed(nodes: Vec) -> (String, Vec) { + let mut content = "".to_owned(); + + for node in nodes { + match node { + FrozenBlob::Content(c) => { + if !content.is_empty() { + panic!("There should not be more than one content node!") + } + content = c; + } + } + } + + let blob = format!("blob {} {}", content.as_bytes().len(), content); + + // Since we have header + content size + content together, lets hash it + let hash = blake3::hash(blob.as_bytes()); + + // And the compress it + let compressed_blob = compress_prepend_size(blob.as_bytes()); + + (hash.to_string(), compressed_blob) +} + +pub fn from_processed(hash: String, processed: Vec) -> Result> { + let uncompressed = decompress_size_prepended(&processed).unwrap(); + + let processed_hash = blake3::hash(&*uncompressed); + + if processed_hash.to_string() != hash.to_string() { + bail!( + "Object hash is '{}' after decompressed, but expected {}", + processed_hash, hash + ); + } + + let raw = String::from_utf8(uncompressed)?; + let (id, content) = raw.split_at("blob 10 ".len()); + let (_, size) = id.split_at("blob ".len()); + let size: usize = size.split_whitespace().next().unwrap().parse()?; + + if size != content.len() { + bail!("Context size is '{}' but expected was '{}'", content.len(), size) + } + + Ok(vec![FrozenBlob::Content(content.to_owned())]) +} diff --git a/core/src/schema/ir/frozen/commit.rs b/core/src/schema/ir/frozen/commit.rs new file mode 100644 index 0000000..c688b99 --- /dev/null +++ b/core/src/schema/ir/frozen/commit.rs @@ -0,0 +1,18 @@ +// Standard Uses + +// Crate Uses + +// External Uses + + +pub enum FrozenCommit { + Header(FrozenHeader), + Tree(String), + Blob(String) +} + +pub enum FrozenHeader { + AuthorId(Option), + Version(semver::Version), +} + diff --git a/core/src/schema/ir/frozen/mod.rs b/core/src/schema/ir/frozen/mod.rs new file mode 100644 index 0000000..9b1e6fa --- /dev/null +++ b/core/src/schema/ir/frozen/mod.rs @@ -0,0 +1,5 @@ +// Relative Modules +pub mod unit; +pub mod commit; +pub mod tree; +pub mod blob; diff --git a/core/src/schema/ir/frozen/tree.rs b/core/src/schema/ir/frozen/tree.rs new file mode 100644 index 0000000..cf2d43a --- /dev/null +++ b/core/src/schema/ir/frozen/tree.rs @@ -0,0 +1,20 @@ +// Standard Uses + +// Crate Uses + +// External Uses + + +pub enum FrozenTree { + +} + +#[allow(unused)] +pub fn to_processed(nodes: Vec) -> String { + todo!() +} + +#[allow(unused)] +pub fn from_processed() -> Vec { + todo!() +} diff --git a/core/src/schema/ir/frozen/unit.rs b/core/src/schema/ir/frozen/unit.rs new file mode 100644 index 0000000..238becd --- /dev/null +++ b/core/src/schema/ir/frozen/unit.rs @@ -0,0 +1,87 @@ +// Standard Uses + +// Crate Uses +use crate::schema::ir::context::SchemaContext; +use crate::schema::ir::compiler::interpreted::primitive::KindValue; + +// External Uses + + +pub type FrozenWhole = (SchemaContext, Vec); + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum FrozenUnit { + // TODO: Are Tags really necessary anymore since we hash Frozen Units by blob, trees and commits? + // Tag(String), + Namespace(String), + Import(String), + Constant { + docstring: Option, + name: String, + kind_value: KindValue + }, + Property { + name: String, + expression: Option + }, + Parameter { + name: String, + default_value: String + }, + ExpressionBlock { + function_calls: Vec + }, + // + Enum { + docstring: Option, + name: String, + variants: Vec + }, + EnumVariant(KindValue), + Settings { + docstring: Option, + name: String, + parameters: Vec, + }, + Struct { + docstring: Option, + parameters: Vec, + name: String, + fields: Vec, + }, + Protocol { + docstring: String, + parameters: Vec, + name: String, + functions: Vec + }, + Function { + docstring: String, + name: String, + synchronous: bool, + direction: Box, + arguments: Vec, + returns: Vec, + throws: Vec + }, + Error { + docstring: Option, + parameters: Vec, + name: String, + message: String, + fields: Vec + }, + Validator { + docstring: Option, + properties: Vec, + name: String, + expression_block: Box + }, + Field { + docstring: Option, + parameters: Vec, + optional: bool, + name: String, + kind_value: KindValue, + } +} diff --git a/core/src/schema/ir/mod.rs b/core/src/schema/ir/mod.rs new file mode 100644 index 0000000..248962c --- /dev/null +++ b/core/src/schema/ir/mod.rs @@ -0,0 +1,5 @@ +// Relative Modules +pub mod context; +pub mod compiler; +pub mod frozen; +pub mod diff; diff --git a/core/src/schema/mod.rs b/core/src/schema/mod.rs new file mode 100644 index 0000000..37a4936 --- /dev/null +++ b/core/src/schema/mod.rs @@ -0,0 +1,3 @@ +// Relative Modules +pub mod idl; +pub mod ir; diff --git a/core/src/utils/codemap.rs b/core/src/utils/codemap.rs new file mode 100644 index 0000000..ab9da34 --- /dev/null +++ b/core/src/utils/codemap.rs @@ -0,0 +1,309 @@ +// Standard Uses +use std::collections::HashMap; +use std::ops::Range; +use std::cmp; +use std::cell::RefCell; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; + +// Crate Uses + +// External Uses + + +/// A unique identifier pointing to a substring in some file. +/// +/// To get back the original string this points to you'll need to look it up +/// in a `CodeMap` or `FileMap`. +#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] +#[derive(Serialize, Deserialize)] +pub struct Span(pub usize); + +impl Span { + #[allow(unused)] + /// Returns the special "dummy" span, which matches anything. This should + /// only be used internally to make testing easier. + pub(crate) fn dummy() -> Span { + Span(0) + } +} + + +/// A mapping of `Span`s to the files in which they are located. +#[derive(Debug, Clone)] +pub struct CodeMap { + next_id: Arc, + files: Vec>, +} + +impl CodeMap { + /// Create a new, empty `CodeMap`. + pub fn new() -> CodeMap { + let next_id = Arc::new(AtomicUsize::new(1)); + let files = Vec::new(); + CodeMap { next_id, files } + } + + /// Add a new file to the `CodeMap` and get back a reference to it. + pub fn insert_file(&mut self, filename: F, contents: C) -> Arc + where F: Into, + C: Into, + { + let filemap = FileMap { + name: filename.into(), + contents: contents.into(), + items: RefCell::new(HashMap::new()), + next_id: Arc::clone(&self.next_id), + }; + let fm = Arc::new(filemap); + self.files.push(Arc::clone(&fm)); + + fm + } + + /// Get the substring that this `Span` corresponds to. + pub fn lookup(&self, span: Span) -> &str { + for filemap in &self.files { + if let Some(substr) = filemap.lookup(span) { + return substr; + } + } + + panic!("Tried to lookup {:?} but it wasn't in any \ + of the FileMaps... This is a bug!", span) + } + + /// The files that this `CodeMap` contains. + pub fn files(&self) -> &[Arc] { + self.files.as_slice() + } +} + +unsafe impl Sync for CodeMap {} +unsafe impl Send for CodeMap {} + +impl Default for CodeMap { + fn default() -> CodeMap { + CodeMap::new() + } +} + +/// A mapping which keeps track of a file's contents and allows you to cheaply +/// access substrings of the original content. +#[derive(Clone, Debug)] +pub struct FileMap { + name: String, + contents: String, + pub(crate) next_id: Arc, + items: RefCell>> +} + +impl FileMap { + /// Get the name of this `FileMap`. + pub fn filename(&self) -> &str { + &self.name + } + + /// Get the entire content of this file. + pub fn contents(&self) -> &str { + &self.contents + } + + /// Lookup a span in this `FileMap`. + /// + /// # Panics + /// + /// If the `FileMap`'s `items` hashmap contains a span, but that span + /// **doesn't** point to a valid substring this will panic. If you ever + /// get into a situation like this then things are almost certainly FUBAR. + pub fn lookup(&self, span: Span) -> Option<&str> { + let range = match self.range_of(span) { + Some(r) => r, + None => return None, + }; + + match self.contents.get(range.clone()) { + Some(substr) => Some(substr), + None => panic!("FileMap thinks it contains {:?}, \ + but the range ({:?}) doesn't point to anything valid!", span, range), + } + } + + /// Get the range corresponding to this span. + pub fn range_of(&self, span: Span) -> Option> { + self.items.borrow().get(&span).cloned() + } +} + +impl FileMap { + /// Ask the `FileMap` to give you the span corresponding to the half-open + /// interval `[start, end)`. + /// + /// # Panics + /// + /// In debug mode, this will panic if either `start` or `end` are outside + /// the source code or if they don't lie on a codepoint boundary. + /// + /// It is assumed that the `start` and `indices` were originally obtained + /// from the file's contents. + pub fn insert_span(&self, start: usize, end: usize) -> Span { + debug_assert!(self.contents.is_char_boundary(start), + "Start doesn't lie on a char boundary"); + debug_assert!(self.contents.is_char_boundary(end), + "End doesn't lie on a char boundary"); + debug_assert!(start < self.contents.len(), + "Start lies outside the content string"); + debug_assert!(end <= self.contents.len(), + "End lies outside the content string"); + + let range = start..end; + + if let Some(existing) = self.reverse_lookup(&range) { + return existing; + } + + let span_id = self.next_id.fetch_add(1, Ordering::Relaxed); + let span = Span(span_id); + + self.items.borrow_mut().insert(span, range); + span + } + + /// We don't want to go and add duplicate spans unnecessarily so we + /// iterate through all existing ranges to see if this one already + /// exists. + fn reverse_lookup(&self, needle: &Range) -> Option { + self.items.borrow() + .iter() + .find(|&(_, range)| range == needle) + .map(|(span, _)| span) + .cloned() + } + + /// Merge two spans to get the span which includes both. + /// + /// As usual, the constraints from `insert_span()` also apply here. If + /// you try to enter two spans from different `FileMap`s, it'll panic. + pub fn merge(&self, first: Span, second: Span) -> Span { + let range_1 = self.range_of(first).expect("Can only merge spans from the same FileMap"); + let range_2 = self.range_of(second).expect("Can only merge spans from the same FileMap"); + + let start = cmp::min(range_1.start, range_2.start); + let end = cmp::max(range_1.end, range_2.end); + + self.insert_span(start, end) + } + + pub fn items(&self) -> &RefCell>> { + &self.items + } +} + +/* +impl FileMap { + /// Register a set of tokenized inputs and turn them into a proper stream + /// of tokens. Note that all the caveats from `insert_span()` also apply + /// here. + pub fn register_tokens(&self, tokens: Vec<(TokenKind, usize, usize)>) -> Vec { + let mut registered = Vec::new(); + + for (kind, start, end) in tokens { + let span = self.insert_span(start, end); + let token = Token::new(span, kind); + registered.push(token); + } + + registered + } +} +*/ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn insert_a_file_into_a_codemap() { + let mut map = CodeMap::new(); + let filename = "foo.rs"; + let content = "Hello World!"; + + assert_eq!(map.files.len(), 0); + let fm = map.insert_file(filename, content); + + assert_eq!(fm.filename(), filename); + assert_eq!(fm.contents(), content); + assert_eq!(map.files.len(), 1); + } + + #[test] + fn get_span_for_substring() { + let mut map = CodeMap::new(); + let src = "Hello World!"; + let fm = map.insert_file("foo.rs", src); + + let start = 2; + let end = 5; + let should_be = &src[start..end]; + + let span = fm.insert_span(start, end); + let got = fm.lookup(span).unwrap(); + assert_eq!(got, should_be); + assert_eq!(fm.range_of(span).unwrap(), start..end); + + let got_from_codemap = map.lookup(span); + assert_eq!(got_from_codemap, should_be); + } + + #[test] + fn spans_for_different_ranges_are_always_unique() { + let mut map = CodeMap::new(); + let src = "Hello World!"; + let fm = map.insert_file("foo.rs", src); + + let mut spans = Vec::new(); + + for start in 0..src.len() { + for end in start..src.len() { + let span = fm.insert_span(start, end); + assert!(!spans.contains(&span), + "{:?} already contains {:?} ({}..{})", + spans, span, start, end); + assert!(span != Span::dummy()); + + spans.push(span); + } + } + } + + #[test] + fn spans_for_identical_ranges_are_identical() { + let mut map = CodeMap::new(); + let src = "Hello World!"; + let fm = map.insert_file("foo.rs", src); + + let start = 0; + let end = 5; + + let span_1 = fm.insert_span(start, end); + let span_2 = fm.insert_span(start, end); + + assert_eq!(span_1, span_2); + } + + #[test] + fn join_multiple_spans() { + let mut map = CodeMap::new(); + let src = "Hello World!"; + let fm = map.insert_file("foo.rs", src); + + let span_1 = fm.insert_span(0, 2); + let span_2 = fm.insert_span(3, 8); + + let joined = fm.merge(span_1, span_2); + let equivalent_range = fm.range_of(joined).unwrap(); + + assert_eq!(equivalent_range.start, 0); + assert_eq!(equivalent_range.end, 8); + } +} diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs new file mode 100644 index 0000000..7609277 --- /dev/null +++ b/core/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod codemap; +pub mod templating; diff --git a/core/src/utils/templating.rs b/core/src/utils/templating.rs new file mode 100644 index 0000000..01eb2c4 --- /dev/null +++ b/core/src/utils/templating.rs @@ -0,0 +1,32 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use eyre::Result; +use handlebars::{Handlebars, RenderError}; +use serde::Serialize; + + +pub trait ResolvePaths { + fn resolve_paths(&self, root: &T) -> Result where Self: Sized; +} + + +pub fn recurse_render(path: &str, entity: &T) + -> std::result::Result + where T: Serialize +{ + let mut reg = Handlebars::new(); + reg.set_strict_mode(true); + + let re = regex::Regex::new(r".*\{\{(.*)\}\}.*").unwrap(); + + let mut res = reg.render_template(path, entity)?; + while re.is_match(&*res) { + res = reg.render_template(&*res, entity)?; + } + + Ok(res) +} + diff --git a/core/tests/autodoc/mod.rs b/core/tests/autodoc/mod.rs new file mode 100644 index 0000000..8d8c539 --- /dev/null +++ b/core/tests/autodoc/mod.rs @@ -0,0 +1,46 @@ +// Standard Uses + +// Local Uses +use comline::schema::ir::frozen::unit::FrozenUnit; + +// External Uses +use once_cell::sync::Lazy; +use comline::autodoc; +use comline::schema::ir::compiler::interpreted::primitive::{KindValue, Primitive}; + + +static PREVIOUS_FROZEN_UNITS: Lazy> = Lazy::new(|| + vec![ + FrozenUnit::Namespace("foobar".to_owned()), + FrozenUnit::Constant { + docstring: None, + name: "DUCKS".to_string(), kind_value: KindValue::Primitive(Primitive::U8(Some(10))), + } + ] +); + +static CURRENT_FROZEN_UNITS: Lazy> = Lazy::new(|| + vec![ + FrozenUnit::Namespace("foo::bar".to_owned()), + FrozenUnit::Constant { + docstring: None, + name: "DUCKS".to_string(), kind_value: KindValue::Primitive(Primitive::U8(Some(15))), + } + ] +); + +#[test] +fn difference_docs() { + let diff_docs = autodoc::document_differences( + &*PREVIOUS_FROZEN_UNITS, &*CURRENT_FROZEN_UNITS + ); + + // "Version change from '0.1.0' to '0.2.0'" \ + pretty_assertions::assert_eq!(diff_docs.major_changes, vec![ + "Namespace changed from 'foobar' to 'foo::bar'" + ]); + + pretty_assertions::assert_eq!(diff_docs.minor_changes, vec![ + r#"Constant 'DUCKS' default value changed from '10' to '15'"# + ]); +} diff --git a/core/tests/idl/ir/mod.rs b/core/tests/idl/ir/mod.rs new file mode 100644 index 0000000..f722d72 --- /dev/null +++ b/core/tests/idl/ir/mod.rs @@ -0,0 +1,44 @@ +// Standard Uses + +// Crate Uses +use comline::schema::ir::frozen::blob; +use comline::schema::ir::frozen::blob::FrozenBlob; + +// External Uses + + + +#[test] +pub fn frozen_nodes_into_processed() { + let nodes = vec![ + FrozenBlob::Content("Hello blob".to_owned()) + ]; + + let (hash, processed) = blob::to_processed(nodes); + + assert_eq!(hash, "1f8473854dc9445b9c55a16202fb191e4b7b969e5521f32a21d884c31d413335"); + assert_eq!( + processed, + vec![ + 18, 0, 0, 0, 240, 3, 98, 108, 111, 98, 32, + 49, 48, 32, 72, 101, 108, 108, 111, 32, 98, 108, 111, 98 + ] + ); +} + + +#[test] +pub fn frozen_nodes_from_processed() { + let (hash, processed) = ( + "1f8473854dc9445b9c55a16202fb191e4b7b969e5521f32a21d884c31d413335".to_owned(), + vec![ + 18, 0, 0, 0, 240, 3, 98, 108, 111, 98, 32, + 49, 48, 32, 72, 101, 108, 108, 111, 32, 98, 108, 111, 98 + ] + ); + + let nodes = blob::from_processed(hash, processed).unwrap(); + + assert_eq!(nodes, vec![FrozenBlob::Content("Hello blob".to_owned())]); +} + diff --git a/core/tests/idl/mod.rs b/core/tests/idl/mod.rs new file mode 100644 index 0000000..1798162 --- /dev/null +++ b/core/tests/idl/mod.rs @@ -0,0 +1,4 @@ +// Relative Imports +mod unit; +mod ir; +// mod stdlib; diff --git a/tests/idl/simple.ids b/core/tests/idl/simple.ids similarity index 67% rename from tests/idl/simple.ids rename to core/tests/idl/simple.ids index bae237c..a2262b6 100644 --- a/tests/idl/simple.ids +++ b/core/tests/idl/simple.ids @@ -1,7 +1,7 @@ // Simple Schema -namespace tests.idl.simple +namespace tests::idl::simple -import std::validators::StringBounds +import std::validators::string_bounds::StringBounds const POWER: u8 = 1 const DEFAULT_NAME: str = f"flower power: {POWER}" @@ -11,18 +11,23 @@ settings Test { forbid_optional_indexing=True } +enum EncryptionAlgorithm { + Bad + Medium +} + enum EncryptionMode { - None - Encrypt + None + Encrypt } /// A message that can be sent through the mail protocol struct Message { - 1# name: str = DEFAULT_NAME - 2# encryption_mode: EncryptionMode = default + name: str = DEFAULT_NAME + encryption_mode: EncryptionMode = default @validators=[StringBounds(min_chars=3 max_chars=12)] - 2# optional recipient: str = "bee" + optional recipient: str = "bee" } /// Throw when sending a message to a missing recipient @@ -30,7 +35,7 @@ error RecipientNotFoundError { message = "Recipient with name {self.recipient} not found" @validators=[StringBounds(min_chars=8 max_chars=16)] - 1# recipient: str + recipient: str } /// Mail API for receiving and sending emails @@ -38,6 +43,6 @@ error RecipientNotFoundError { protocol Mail { @timeout_ms=1000 - 1# function send_message(message: Message) -> str + function send_message(message: Message) -> str ! RecipientNotFoundError(function.message.recipient) } diff --git a/core/tests/idl/stdlib/mod.rs b/core/tests/idl/stdlib/mod.rs new file mode 100644 index 0000000..20e17cb --- /dev/null +++ b/core/tests/idl/stdlib/mod.rs @@ -0,0 +1,2 @@ +// Relative Modules +mod validators; diff --git a/core/tests/idl/stdlib/validators.rs b/core/tests/idl/stdlib/validators.rs new file mode 100644 index 0000000..76fe45c --- /dev/null +++ b/core/tests/idl/stdlib/validators.rs @@ -0,0 +1,101 @@ +// Standard Uses +use std::path::Path; + +// Local Uses + +// External Uses +use once_cell::sync::Lazy; +use comline::schema::{idl, ir}; +use comline::schema::idl::ast::unit::{ASTUnit, SourcedWhole}; +use comline::utils::codemap::{CodeMap, Span}; + + +static STRING_BOUNDS_SCHEMA: &str = "src/langlib/validators/string_bounds.ids"; +static STRING_BOUNDS_SCHEMA_PATH: Lazy<&Path> = Lazy::new(|| + Path::new(STRING_BOUNDS_SCHEMA) +); + +#[test] +fn parse_string_bounds_unit() { + let unit = idl::parser_new::from_path(&STRING_BOUNDS_SCHEMA_PATH).unwrap(); + + pretty_assertions::assert_eq!( + unit.1, expected_string_bounds_validator_ast(STRING_BOUNDS_SCHEMA).1 + ) +} + +#[test] +fn parse_string_bounds_whole_unit() { + let unit = idl::parser_new::from_path(&STRING_BOUNDS_SCHEMA_PATH).unwrap(); + + let context = ir::context::SchemaContext::with_main(unit); + + pretty_assertions::assert_eq!( + context.schema.1, expected_string_bounds_validator_ast(STRING_BOUNDS_SCHEMA).1 + ) + + /* + let interpreted = compiler::interpret_unit(context); + + println!("{:?}", interpreted); + */ +} + +#[allow(unused)] +#[test] +fn compile_string_bounds_unit() { + let unit = idl::parser_new::from_path(&STRING_BOUNDS_SCHEMA_PATH).unwrap(); + + let context = ir::context::SchemaContext::with_main_no_std(unit); + + /* + let frozen_unit = interpreter::interpret_unit(context).unwrap(); + + pretty_assertions::assert_eq!( + frozen_unit, vec![ + FrozenUnit::Namespace("std::validators::string_bounds".to_owned()), + ] + ); + */ +} + +#[allow(unused)] +fn expected_string_bounds_validator_ast(path: &str) -> SourcedWhole { + ( + CodeMap::new(), + vec![ + (Span(1), ASTUnit::Namespace( + Span(2), "std::validators::string_bounds".to_owned()) + ), + (Span(3), ASTUnit::Validator { + docstring: vec![ + (Span(1), ASTUnit::Docstring { + variable: None, + description: + " Checks if a string length is between a minimum and a maximum".to_owned(), + }), + (Span(1), ASTUnit::Docstring { + variable: Some("min_chars".to_owned()), + description: "Minimum length of the string".to_owned(), + }), + (Span(1), ASTUnit::Docstring { + variable: Some("max_chars".to_owned()), + description: "Maximum length of the string".to_owned(), + }), + ], + properties: vec![ + (Span(1), ASTUnit::Property { + name: (Span(1), "min_chars".to_string()), expression: None, + }), + (Span(1), ASTUnit::Property { + name: (Span(1), "max_chars".to_string()), expression: None, + }) + ], + name: (Span(1), "StringBounds".to_string()), + expression_block: Box::new( + (Span(1), ASTUnit::ExpressionBlock { function_calls: vec![] }) + ), + }), + ], + ) +} diff --git a/core/tests/idl/unit.rs b/core/tests/idl/unit.rs new file mode 100644 index 0000000..13faa90 --- /dev/null +++ b/core/tests/idl/unit.rs @@ -0,0 +1,249 @@ +// Standard Uses +use std::path::Path; + +// Local Uses + +// External Uses +use comline::schema::{idl, ir}; +use comline::schema::idl::constants::SCHEMA_EXTENSION; +use comline::schema::idl::ast::unit::ASTUnit; +use comline::utils::codemap::Span; + + +#[test] +fn from_raw_to_unit() { + let path = &format!("tests/idl/simple.{}", SCHEMA_EXTENSION); + let path = Path::new(path); + let raw = std::fs::read_to_string(path).unwrap(); + let sourced = idl::parser_new::parse_source( + raw, path.to_str().unwrap().to_owned() + ).unwrap(); + + /* + pretty_assertions::assert_eq!( + sourced.1, vec![ + (Span(1), ASTUnit::Namespace(Span(2), "tests::idl::simple".to_owned())), + (Span(3), (ASTUnit::Import( + Span(4), "std::validators::string_bounds::StringBounds".to_string())) + ), + (Span(5), ASTUnit::Constant { + docstring: vec![], + name: (Span(6), "POWER".to_owned()), + kind: (Span(7), "u8".to_owned()), + default_value: Some((Span(8), "1".to_owned())), + }), + (Span(9), ASTUnit::Constant { + docstring: vec![], + name: (Span(10), "DEFAULT_NAME".to_owned()), + kind: (Span(11), "str".to_owned()), + default_value: Some((Span(12), "f\"flower power: {POWER}\"".to_owned())), + }), + (Span(13), ASTUnit::Settings { + docstring: vec![], + name: (Span(14), "Test".to_owned()), + parameters: vec![ + (Span(15), ASTUnit::Parameter { + name: (Span(16), "forbid_indexing".to_string()), + default_value: (Span(17), "True".to_string()), + }), + (Span(18), ASTUnit::Parameter { + name: (Span(19), "forbid_optional_indexing".to_string()), + default_value: (Span(20), "True".to_string()), + }) + ], + }), + (Span(21), ASTUnit::Enum { + docstring: vec![], + name: (Span(22), "EncryptionAlgorithm".to_owned()), + variants: vec![ + (Span(23), ASTUnit::EnumVariant { + name: (Span(24), "Bad".to_owned()), kind: None + }), + (Span(25), ASTUnit::EnumVariant { + name: (Span(26), "Medium".to_owned()), kind: None + }) + ] + }), + (Span(27), ASTUnit::Enum { + docstring: vec![], + name: (Span(28), "EncryptionMode".to_string()), + variants: vec![ + (Span(29), ASTUnit::EnumVariant { + name: (Span(30), "None".to_string()), kind: None + }), + (Span(31), ASTUnit::EnumVariant { + name: (Span(32), "Encrypt".to_string()), kind: None + }) + ], + }), + (Span(33), ASTUnit::Struct { + docstring: vec![ + (Span(34), ASTUnit::Docstring { + variable: None, + description: " A message that can be sent through the mail protocol".to_owned(), + }), + ], + parameters: vec![], + name: (Span(35), "Message".to_owned()), + fields: vec![ + (Span(36), ASTUnit::Field { + docstring: vec![], + parameters: vec![], + optional: false, + name: "name".to_owned(), + kind: "str".to_owned(), + default_value: Some("DEFAULT_NAME".to_owned()), + }), + (Span(37), ASTUnit::Field { + docstring: vec![], + parameters: vec![], + optional: false, + name: "encryption_mode".to_owned(), + kind: "EncryptionMode".to_owned(), + default_value: Some("default".to_owned()), + }), + (Span(38), ASTUnit::Field { + docstring: vec![], + parameters: vec![], + optional: true, + name: "recipient".to_owned(), + kind: "str".to_owned(), + default_value: Some("\"bee\"".to_owned()), + }) + ], + }), + (Span(39), ASTUnit::Error { + docstring: vec![ + (Span(40), ASTUnit::Docstring { variable: None, + description: " Throw when sending a message to a missing recipient".to_string() } + ), + ], + parameters: vec![ + (Span(42), ASTUnit::Parameter { + name: (Span(43), "message".to_owned()), + default_value: ( + Span(44), + "\"Recipient with name {self.recipient} not found\"".to_owned() + ), + }) + ], + name: (Span(41), "RecipientNotFoundError".to_owned()), + properties: vec![], + fields: vec![ + (Span(45), ASTUnit::Field { + docstring: vec![], + parameters: vec![], + optional: false, + name: "recipient".to_string(), + kind: "str".to_string(), + default_value: None, + }) + ], + }), + (Span(46), ASTUnit::Protocol { + docstring: vec![ + (Span(47), ASTUnit::Docstring { + variable: None, + description: " Mail API for receiving and sending emails".to_string(), + }) + ], + parameters: vec![ + (Span(48), ASTUnit::Parameter { + name: (Span(49), "provider".to_owned()), + default_value: (Span(50), "Any".to_owned(), ), + }), + ], + name: (Span(51), "Mail".to_owned()), + functions: vec![ + (Span(52), ASTUnit::Function { + docstring: vec![], + parameters: vec![ + (Span(53), ASTUnit::Parameter { + name: (Span(54), "timeout_ms".to_string()), + default_value: (Span(55), "1000".to_string()), + }) + ], + name: (Span(56), "send_message".to_owned()), + asynchronous: None, + arguments: vec![ + (Span(57), ASTUnit::Argument { + name: Some((Span(58), "message".to_owned())), + kind: (Span(59), "Message".to_string()), + }) + ], + returns: vec![ + (Span(60), "str".to_owned()) + ], + throws: vec![ + (Span(61), "RecipientNotFoundError(function.message.recipient)\n".to_owned()) + ], + }) + ], + }) + ] + ) + */ +} + + +#[allow(unused)] +#[test] +fn compile_unit() { + let path = &format!("tests/idl/simple.{}", SCHEMA_EXTENSION); + let path = Path::new(path); + let unit = idl::parser_new::from_path(path).unwrap(); + + let context = ir::context::SchemaContext::with_main(unit); + + /* + let frozen_unit = interpreter::interpret_context(context).unwrap(); + + pretty_assertions::assert_eq!( + frozen_unit, vec![ + FrozenUnit::Namespace("tests::idl::simple".to_string()), + FrozenUnit::Import("std::validators::string_bounds::StringBounds".to_string()), + FrozenUnit::Constant { + docstring: None, + name: "POWER".to_string(), + kind_value: KindValue::Primitive(Primitive::U8(Some(1))), + }, + FrozenUnit::Constant { + docstring: None, + name: "DEFAULT_NAME".to_string(), + kind_value: KindValue::Primitive(Primitive::String( + Some("f\"flower power: {POWER}\"".to_string()) + )), + }, + FrozenUnit::Enum { + docstring: None, + name: "EncryptionAlgorithm".to_string(), variants: vec![ + FrozenUnit::EnumVariant(KindValue::EnumVariant( + "Bad".to_owned(), None + )), + FrozenUnit::EnumVariant(KindValue::EnumVariant( + "Medium".to_owned(), None + )) + ], + }, + FrozenUnit::Enum { + docstring: None, + name: "EncryptionMode".to_string(), variants: vec![ + FrozenUnit::EnumVariant(KindValue::EnumVariant( + "None".to_owned(), None) + ), + FrozenUnit::EnumVariant(KindValue::EnumVariant( + "Encrypt".to_owned(), + None + // TODO; The reference bellow is a feature that needs thought, uncomment later + /* + Some(Box::from(KindValue::EnumVariant( + "Medium".to_string(), None + ))) + */ + )) + ] + } + ] + ); + */ +} diff --git a/core/tests/mod.rs b/core/tests/mod.rs new file mode 100644 index 0000000..b5241a6 --- /dev/null +++ b/core/tests/mod.rs @@ -0,0 +1,3 @@ +mod idl; +mod package; +mod autodoc; diff --git a/core/tests/package/build.rs b/core/tests/package/build.rs new file mode 100644 index 0000000..d46544c --- /dev/null +++ b/core/tests/package/build.rs @@ -0,0 +1,15 @@ +// Standard Uses + +// Crate Uses +use crate::package::TEST_PACKAGE_DIR; + +// External Uses +use comline::project; + + +#[test] +fn build_package() { + let built = project::build::build(&TEST_PACKAGE_DIR); + + built.unwrap(); +} diff --git a/core/tests/package/compile.rs b/core/tests/package/compile.rs new file mode 100644 index 0000000..aea212c --- /dev/null +++ b/core/tests/package/compile.rs @@ -0,0 +1,18 @@ +// Standard Uses + +// Crate Uses +use crate::package::TEST_PACKAGE_CFG_PATH; + +// External Uses +use comline::project::ir::compiler::Compile; +use comline::project::ir::interpreter::Interpreter; + + +#[allow(unused)] +#[test] +fn compile_package() { + let compiled = Interpreter::from_origin(&TEST_PACKAGE_CFG_PATH); + + todo!() + // pretty_assertions::assert_eq!(compiled, ()); +} diff --git a/core/tests/package/mod.rs b/core/tests/package/mod.rs new file mode 100644 index 0000000..327721a --- /dev/null +++ b/core/tests/package/mod.rs @@ -0,0 +1,43 @@ +// Relative Modules +mod parse; +mod compile; +mod build; + +// Standard Uses +use std::path::{Path, PathBuf}; + +// Crate Uses + +// External Uses +use once_cell::sync::Lazy; +use comline::project::idl::constants::CONGREGATION_EXTENSION; + + +static TEST_PACKAGE_DIR: Lazy<&Path> = Lazy::new(|| Path::new("tests/package/test/")); +static TEST_PACKAGE_CFG_PATH: Lazy = Lazy::new(|| + TEST_PACKAGE_DIR.join(format!("config.{}", CONGREGATION_EXTENSION)) +); + + +/* +#[test] +fn parse_package_project() { + let project = project::idl::parser::from_path(&TEST_PACKAGE_CFG_PATH).unwrap(); + + pretty_assertions::assert_eq!( + project, + ( + VIndex { + meta: UnitIndex::Index { path: "".to_string(), source: "".to_string() }, + nodes: vec![] + }, + vec![ + ASTUnit::Congregation("".to_owned()), + ASTUnit::Dependencies(vec![ + + ]) + ] + ) + ) +} +*/ diff --git a/core/tests/package/parse.rs b/core/tests/package/parse.rs new file mode 100644 index 0000000..9a6a4ca --- /dev/null +++ b/core/tests/package/parse.rs @@ -0,0 +1,63 @@ +// Standard Uses +use std::ops::Range; + +// Crate Uses +use crate::package::TEST_PACKAGE_CFG_PATH; + +// External Uses +use comline::project; +use comline::project::idl::ast::{AssignmentUnit, ASTUnit, ListItem}; +use comline::utils::codemap::Span; + + +#[test] +fn parse_package_project_new() { + let project = project::idl::parser_new::from_path(&TEST_PACKAGE_CFG_PATH) + .unwrap(); + + let expected = vec![ + (Span(1), ASTUnit::Namespace(Span(2), "test".to_owned())), + (Span(3), ASTUnit::Assignment { + name: (Span(4), "specification_version".to_owned()), + value: (Span(5), AssignmentUnit::Number(1)) + }), + (Span(6), ASTUnit::Assignment { + name: (Span(7), "schema_paths".to_owned()), + value: (Span(8), AssignmentUnit::List(vec![ + ListItem::String(Span(9), "ping.ids".to_owned()) + ])) + }) + ]; + + pretty_assertions::assert_eq!(expected, project.1); + + let expected_span = vec![ + Range { start: 0, end: 26 }, + Range { start: 13, end: 26 }, + Range { start: 26, end: 52 }, + Range { start: 27, end: 48 }, + Range { start: 51, end: 52 }, + Range { start: 52, end: 88 }, + Range { start: 54, end: 66 }, + Range { start: 69, end: 88 }, + Range { start: 76, end: 84 }, + ]; + + // let contents = project.0.files().first().unwrap().contents(); + let mut i = 0; + while i < project.0.files().first().unwrap().items().borrow().values().len() { + i += 1; + + /* + let span = &expected_span[i - 1]; + println!("next is {:?}", project.0.files()[0].range_of(Span(i+1))); + println!("{:?} - {:?}", span, contents.get(span.clone()).unwrap()); + */ + + assert_eq!( + Some(&expected_span[i - 1]), + project.0.files()[0].range_of(Span(i)).as_ref() + ); + }; +} + diff --git a/core/tests/package/test/.frozen/index b/core/tests/package/test/.frozen/index new file mode 100644 index 0000000..f406659 --- /dev/null +++ b/core/tests/package/test/.frozen/index @@ -0,0 +1 @@ +80c3e72f31cae7becda6225f1dc70eab591241fc9daefcb30721efddb346243a0.0.1@d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 \ No newline at end of file diff --git a/core/tests/package/test/.frozen/objects/project/d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 b/core/tests/package/test/.frozen/objects/project/d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 new file mode 100644 index 0000000..ceff3be --- /dev/null +++ b/core/tests/package/test/.frozen/objects/project/d4f0cac8cbf4425b5f4a54d56618fb46d2b5eda086c7eed52ac459ad3d22f2a1 @@ -0,0 +1 @@ +NamespacetestSpecificationVersionSchemaPaththing.idsDependencytesttesttest \ No newline at end of file diff --git a/core/tests/package/test/config.idp b/core/tests/package/test/config.idp new file mode 100644 index 0000000..b274980 --- /dev/null +++ b/core/tests/package/test/config.idp @@ -0,0 +1,22 @@ +congregation test +specification_version = 1 + +schema_paths = [ + "ping.ids" +] + +code_generation = { + languages = { + rust#1.70.0 = { package_versions=[all] } + } +} + +publish_targets = { + official = std::publish::index + dev_test_index = "https://newwars.colean.cc/comline_public/" + my_test_index = { + url="https://newwars.colean.cc/my_cl_index/" + method="ssh" + } +} + diff --git a/src/stdlib/validators/mod.rs b/core/tests/package/test/generated/0.0.1/rust/1.70.0/health.rs similarity index 100% rename from src/stdlib/validators/mod.rs rename to core/tests/package/test/generated/0.0.1/rust/1.70.0/health.rs diff --git a/core/tests/package/test/health.ids b/core/tests/package/test/health.ids new file mode 100644 index 0000000..beb7bfd --- /dev/null +++ b/core/tests/package/test/health.ids @@ -0,0 +1,13 @@ +// Health Schema +namespace test::idl::health + +struct Capabilities { + names: str[] +} + +/// Health check an address +@provider=Any +protocol HealthCheck { + function alive() -> bool +} + diff --git a/core/tests/package/test/ping.ids b/core/tests/package/test/ping.ids new file mode 100644 index 0000000..87362ec --- /dev/null +++ b/core/tests/package/test/ping.ids @@ -0,0 +1,15 @@ +// Ping Schema +namespace tests::idl::ping + +const LOW_PING_RATE:u16 = 20; + + +/// Ping another address +@provider=Any +protocol Ping { + function ping() -> bool + + @timeout_ms=1000 + function ping_limit() -> bool +} + diff --git a/core_stdlib/Cargo.toml b/core_stdlib/Cargo.toml new file mode 100644 index 0000000..9579569 --- /dev/null +++ b/core_stdlib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "comline-core-stdlib" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/core_stdlib/src/lib.rs b/core_stdlib/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/core_stdlib/src/package/config.idp b/core_stdlib/src/package/config.idp new file mode 100644 index 0000000..935f85e --- /dev/null +++ b/core_stdlib/src/package/config.idp @@ -0,0 +1,3 @@ +congregation std + +schema_paths = auto diff --git a/core_stdlib/src/package/validators/string_bounds.ids b/core_stdlib/src/package/validators/string_bounds.ids new file mode 100644 index 0000000..cd73909 --- /dev/null +++ b/core_stdlib/src/package/validators/string_bounds.ids @@ -0,0 +1,29 @@ +namespace validators::string_bounds + +/// Checks if a string length is between a minimum and a maximum +/// @min_chars: Minimum length of the string +/// @max_chars: Maximum length of the string +validator StringBounds { + min_chars: u32 = u32::MIN; + max_chars: u32 = 1024; + + validate { + assert( + value.length > params.max_chars or value.length < params.min_chars, + "String {value.name} must be more than {params.min_chars} and less than {params.max_chars}" + ) + } +} + +/* +assert( + value.length < params.max_chars + "String length {value.name} must be less than {params.max_chars} chars +) + +assert( + value.length > params.min_chars, + "String length {value.name} must be more than {params.min_chars} chars +) +*/ + diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..5377340 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +# Docs build +.cache/ diff --git a/docs/README.md b/docs/README.md index 1e38211..425c303 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,3 +2,6 @@ ## Resource Links +Inspiration for CAS(Content Addressable Storage) +for Schemas and Project Units; https://git-scm.com/book/en/v2/Git-Internals-Git-Objects + diff --git a/docs/docs/ir/cas.md b/docs/docs/ir/cas.md new file mode 100644 index 0000000..a9695e7 --- /dev/null +++ b/docs/docs/ir/cas.md @@ -0,0 +1,16 @@ +# Content Addressable Storage (CAS) +Our CAS is very similar to [Git's one](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects), +with a few differences that make more sense for versioning of a +project multiple API versions and specification, commit author metadata (and more planned), +and more so on (which is planned to be explained in the future) + + +## Types + + +### Blob + +### Tree + +### Commit + diff --git a/docs/docs/ir/index.md b/docs/docs/ir/index.md index df6f151..25a519c 100644 --- a/docs/docs/ir/index.md +++ b/docs/docs/ir/index.md @@ -1,3 +1,2 @@ # Summary - diff --git a/docs/docs/planning/code-generation/compiled-languages.md b/docs/docs/planning/code-generation/compiled-languages.md new file mode 100644 index 0000000..0e72ddc --- /dev/null +++ b/docs/docs/planning/code-generation/compiled-languages.md @@ -0,0 +1,19 @@ +# Compiled Languages Codegen + + + +## Generated Code +Generates the respective code for the languages: + +=== "Rust" + + ```rs + trait Message { + fn receiver(&self) -> String; + fn subject(&self) -> String; + fn message(&self) -> String; + } + ``` + + + diff --git a/docs/docs/planning/code-generation/dynamic-languages.md b/docs/docs/planning/code-generation/dynamic-languages.md new file mode 100644 index 0000000..79881e2 --- /dev/null +++ b/docs/docs/planning/code-generation/dynamic-languages.md @@ -0,0 +1,35 @@ +# Dynamic Languages Codegen + + +## Generated Code +Generates the respective code for the languages: + +=== "Python" + + ```py + class Message(Protocol): + receiver: string + subject: string + message: string + ``` + +=== "TypeScript" + + ```ts + interface Message { + receiver: string + subject: string + message: string + } + ``` + +=== "Luau" + + ```luau + type Message = { + receiver: string, + subject: string, + message: string + } + ``` + diff --git a/docs/docs/planning/code-generation/index.md b/docs/docs/planning/code-generation/index.md new file mode 100644 index 0000000..3c97488 --- /dev/null +++ b/docs/docs/planning/code-generation/index.md @@ -0,0 +1,17 @@ +# Code Generation + +From the following schema below: +=== "IDL" + ```ids + struct Message { + receiver: string + subject: String + message: String + } + ``` + + +Lets see how it will be represented in the different kinds of languages: + - [Compiled Languages](compiled-languages.md) + - [Dynamic Languages](dynamic-languages.md) + diff --git a/docs/docs/planning/coge-generaiton.md b/docs/docs/planning/coge-generaiton.md index 53060a2..c0966f5 100644 --- a/docs/docs/planning/coge-generaiton.md +++ b/docs/docs/planning/coge-generaiton.md @@ -10,38 +10,3 @@ From the following schema: receiver: string } ``` - -#### Generation for Compiled Languages -Generates the respective code for the languages: - -=== "Rust" - ```rs - trait Message { - fn receiver() -> String; - fn subject() -> String; - fn message() -> String; - } - ``` - - -#### Generation for Dynamic Languages -Generates the respective code for the languages: - -=== "Python" - - ```py - class Message(Protocol): - receiver: string - subject: string - message: string - ``` - -=== "TypeScript" - - ```ts - interface Message { - receiver: string - subject: string - message: string - } - ``` diff --git a/docs/docs/planning/ir-generation.md b/docs/docs/planning/ir-generation.md index e0405d7..78be0b4 100644 --- a/docs/docs/planning/ir-generation.md +++ b/docs/docs/planning/ir-generation.md @@ -3,7 +3,7 @@ When compiling a schema, the schemas imported within it and details like metadata, state and so on are written into a lockfile `state.lock` -This lockfile will contain freezed information about evertyhing to be worried, +This lockfile will contain frozen information about everything to be worried, the goal being to preserve relations, state, versioning of the schema(and related schemas) past and present diff --git a/docs/docs/planning/syntax.md b/docs/docs/planning/syntax.md deleted file mode 100644 index 5e899a8..0000000 --- a/docs/docs/planning/syntax.md +++ /dev/null @@ -1,2 +0,0 @@ -# Syntax Proposals - diff --git a/docs/docs/planning/syntax/proposal #1.md b/docs/docs/planning/syntax/proposal #1.md deleted file mode 100644 index b666c96..0000000 --- a/docs/docs/planning/syntax/proposal #1.md +++ /dev/null @@ -1,10 +0,0 @@ -# Syntax Proposal #1 - - -```cpp - -``` - - -## Compilation into IR -On diff --git a/docs/docs/proposals/auto_documentation.md b/docs/docs/proposals/auto_documentation.md new file mode 100644 index 0000000..94d4ee8 --- /dev/null +++ b/docs/docs/proposals/auto_documentation.md @@ -0,0 +1,8 @@ +# Automatic Documentation +Since the change and versioning of APIs is incremental due to the nature of the CAS, +most components of the Schema and the Project files can be somewhat automatically documented + +This automatic documentation should be human-readable, try to +make sense for any kind of reader, and it should also be a optional tool, +the project could set rules on how it wants to use it and when. + diff --git a/docs/docs/proposals/index.md b/docs/docs/proposals/index.md new file mode 100644 index 0000000..09fdd79 --- /dev/null +++ b/docs/docs/proposals/index.md @@ -0,0 +1,4 @@ +# Proposals +Here will be any kind of proposals for the project, from the authors and contributors +that got minimally triaged for fitting context + diff --git a/docs/shell.nix b/docs/shell.nix new file mode 100644 index 0000000..e69de29 diff --git a/examples/simple_gen.rs b/examples/codegen/simple_gen.rs similarity index 100% rename from examples/simple_gen.rs rename to examples/codegen/simple_gen.rs diff --git a/package-manager-server/Cargo.toml b/package-manager-server/Cargo.toml new file mode 100644 index 0000000..c3bd474 --- /dev/null +++ b/package-manager-server/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "comline-package-manager-server" +version = "0.1.0" +edition = "2021" + + +[dependencies] +askama = "0.12.0" +axum = "0.6.19" +once_cell = "1.18.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.68" +tokio = { version = "1.29.1", features = ["full"] } +toml = "0.7.6" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/package-manager-server/README.md b/package-manager-server/README.md new file mode 100644 index 0000000..45a2bb8 --- /dev/null +++ b/package-manager-server/README.md @@ -0,0 +1,11 @@ +## Reference Links +https://github.com/maxcountryman/axum-login +https://github.com/tokio-rs/axum/blob/main/examples/templates/src/main.rs +https://github.com/maxcountryman/axum-login/tree/main/examples/login-with-role +https://docs.rs/ssh-key/latest/ssh_key/ +https://docs.rs/thrussh/latest/thrussh/ +https://djc.github.io/askama/askama.html + + +## Relevant resources +https://doc.rust-lang.org/cargo/reference/registries.html diff --git a/package-manager-server/src/main.rs b/package-manager-server/src/main.rs new file mode 100644 index 0000000..234297a --- /dev/null +++ b/package-manager-server/src/main.rs @@ -0,0 +1,19 @@ +// Relative Modules +mod storage; +mod rest; +mod web_ui; + +// Standard Uses + +// Crate Uses + +// External Uses + + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt().init(); + storage::init(); + + rest::init().await; +} diff --git a/package-manager-server/src/rest/mod.rs b/package-manager-server/src/rest/mod.rs new file mode 100644 index 0000000..f129dbe --- /dev/null +++ b/package-manager-server/src/rest/mod.rs @@ -0,0 +1,43 @@ +// Relative Modules +mod user; +mod publish; +mod package; + +// Standard Uses +use std::net::SocketAddr; + +// Crate Uses +use crate::web_ui; + +// External Uses +use axum::Router; +use axum::routing::post; +use once_cell::sync::OnceCell; + + +pub static ADDRESS: OnceCell = OnceCell::new(); +pub fn web_address() -> String { + format!("http://{}", ADDRESS.get().unwrap()) +} + +pub async fn init() { + let app = Router::new(); + let app = register_routes(app); + let app = web_ui::register_routes(app); + let app = package::register_routes(app); + + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + ADDRESS.get_or_init(|| addr.to_string()); + + tracing::info!("listening on http://{}/", addr); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); +} + +fn register_routes(router: Router) -> Router { + router + .route("/api/users/register", post(user::create_user)) + .route("/api/publish/package", post(publish::publish_package)) +} diff --git a/package-manager-server/src/rest/package.rs b/package-manager-server/src/rest/package.rs new file mode 100644 index 0000000..5a6b8cc --- /dev/null +++ b/package-manager-server/src/rest/package.rs @@ -0,0 +1,43 @@ +// Standard Uses + +// Crate Uses +use crate::storage::FROZEN_PROJECT_RELATIVE_DIR; +use crate::rest::web_address; + +// External Uses +use axum::{extract, http::StatusCode, Router}; +use axum::routing::get; +use serde::Deserialize; + + +/* +pub async fn find_package_url(Json(payload): Json) -> (StatusCode, Json) { + let package_url = "".to_owned(); + + (StatusCode::CREATED, Json(package_url)) +} +*/ + +#[allow(unused)] +pub async fn find_package_url( + extract::Path((name, version)): extract::Path<(String, String)> +) -> (StatusCode, String) { + let url = format!( + "{}/{}/{}{}", + web_address(), FROZEN_PROJECT_RELATIVE_DIR, name, version + ); + + (StatusCode::OK, url) +} + + +#[allow(unused)] +#[derive(Deserialize)] +pub struct PackageMeta { + name: String, version: String +} + +pub fn register_routes(router: Router) -> Router { + router + .route("/api/package/:name/:version", get(find_package_url)) +} diff --git a/package-manager-server/src/rest/publish.rs b/package-manager-server/src/rest/publish.rs new file mode 100644 index 0000000..ffc3581 --- /dev/null +++ b/package-manager-server/src/rest/publish.rs @@ -0,0 +1,23 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use axum::{http::StatusCode, Json}; +use serde::Deserialize; + + +#[allow(unused)] +pub async fn publish_package(Json(payload): Json) -> (StatusCode, Json) { + let package_url = "".to_owned(); + + (StatusCode::CREATED, Json(package_url)) +} + + +#[allow(unused)] +#[derive(Deserialize)] +pub struct PackageDetails { + repository_url: String +} + diff --git a/package-manager-server/src/rest/user.rs b/package-manager-server/src/rest/user.rs new file mode 100644 index 0000000..97956f1 --- /dev/null +++ b/package-manager-server/src/rest/user.rs @@ -0,0 +1,29 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use axum::{http::StatusCode, Json}; +use serde::{Deserialize, Serialize}; + + +pub async fn create_user(Json(payload): Json) -> (StatusCode, Json) { + let user = User { + id: 1337, + username: payload.username, + }; + + (StatusCode::CREATED, Json(user)) +} + +#[derive(Deserialize)] +pub struct CreateUser { + username: String, +} + +#[derive(Serialize)] +pub struct User { + id: u64, + username: String, +} + diff --git a/package-manager-server/src/storage/mod.rs b/package-manager-server/src/storage/mod.rs new file mode 100644 index 0000000..7328258 --- /dev/null +++ b/package-manager-server/src/storage/mod.rs @@ -0,0 +1,33 @@ +// Relative Modules +pub mod projects; +pub mod users; + +// Standard Uses +use std::path::Path; + +// Crate Uses + +// External Uses +use once_cell::sync::Lazy; + + +pub const FROZEN_PROJECT_RELATIVE_DIR: &str = "storage/projects/"; + +#[allow(unused)] +static FROZEN_PROJECTS_DIR: Lazy = Lazy::new(|| + format!("{}/{}", std::env::current_dir().unwrap().to_str().unwrap(), FROZEN_PROJECT_RELATIVE_DIR) +); + +static FROZEN_PROJECTS_PATH: Lazy<&Path> = Lazy::new(|| { + let path = Path::new(&*FROZEN_PROJECTS_DIR); + + if !path.exists() { std::fs::create_dir_all(path).unwrap(); } + + path +}); + + +pub fn init() { + projects::init(); +} + diff --git a/package-manager-server/src/storage/projects.rs b/package-manager-server/src/storage/projects.rs new file mode 100644 index 0000000..c342331 --- /dev/null +++ b/package-manager-server/src/storage/projects.rs @@ -0,0 +1,20 @@ +// Standard Uses + +// Crate Uses + +// External Uses + + +pub fn init() { +} + + +pub struct ProjectInfo { + +} + +#[allow(unused)] +pub fn find_project(name: String) -> Option { + Some(ProjectInfo {}) +} + diff --git a/package-manager-server/src/storage/users.rs b/package-manager-server/src/storage/users.rs new file mode 100644 index 0000000..a42a5d2 --- /dev/null +++ b/package-manager-server/src/storage/users.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External USes + diff --git a/package-manager-server/src/web_ui/mod.rs b/package-manager-server/src/web_ui/mod.rs new file mode 100644 index 0000000..c5f95ac --- /dev/null +++ b/package-manager-server/src/web_ui/mod.rs @@ -0,0 +1,29 @@ +// Relative Modules +mod package; +mod template; + +// Standard Uses + +// Crate Uses +use crate::web_ui::package::package_show; + +// External Uses +use axum::Router; +use axum::routing::get; +use axum::response::Html; + + +pub fn register_routes(router: Router) -> Router { + router + .route("/", get(root)) + .route("/packages", get(packages)) + .route("/package/:name", get(package_show)) +} + +async fn root() -> Html<&'static str> { + Html(include_str!("../../templates/index.html")) +} + +async fn packages() -> Html<&'static str> { + Html(include_str!("../../templates/packages.html")) +} diff --git a/package-manager-server/src/web_ui/package.rs b/package-manager-server/src/web_ui/package.rs new file mode 100644 index 0000000..b50097a --- /dev/null +++ b/package-manager-server/src/web_ui/package.rs @@ -0,0 +1,28 @@ +// Standard Uses + +// Crate Uses +use crate::storage; +use crate::storage::projects::ProjectInfo; +use crate::web_ui::template::HtmlTemplate; + +// External Uses +use axum::extract; +use axum::response::IntoResponse; +use askama::Template; + + +#[derive(Template)] +#[template(path = "package.html")] +struct PackageTemplate { + name: String, + info: Option +} + + +pub async fn package_show(extract::Path(name): extract::Path) -> impl IntoResponse { + let info = storage::projects::find_project(name.clone()); + + let template = PackageTemplate { name, info, }; + + HtmlTemplate(template) +} diff --git a/package-manager-server/src/web_ui/template.rs b/package-manager-server/src/web_ui/template.rs new file mode 100644 index 0000000..97209e5 --- /dev/null +++ b/package-manager-server/src/web_ui/template.rs @@ -0,0 +1,24 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use axum::http::StatusCode; +use axum::response::{Html, IntoResponse, Response}; +use askama::Template; + + +pub struct HtmlTemplate(pub T); + +impl IntoResponse for HtmlTemplate where T: Template, +{ + fn into_response(self) -> Response { + match self.0.render() { + Ok(html) => Html(html).into_response(), + Err(err) => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to render template. Error: {}", err), + ).into_response(), + } + } +} \ No newline at end of file diff --git a/package-manager-server/templates/index.html b/package-manager-server/templates/index.html new file mode 100644 index 0000000..391db43 --- /dev/null +++ b/package-manager-server/templates/index.html @@ -0,0 +1,25 @@ + + + + + Comline - Package Manager Registry +
+

Comline Project Indexer

+

Package manager server registry for packages

+
+ + +
+ Here you can find packages for comline +
+ + + diff --git a/package-manager-server/templates/package.html b/package-manager-server/templates/package.html new file mode 100644 index 0000000..1490aa9 --- /dev/null +++ b/package-manager-server/templates/package.html @@ -0,0 +1,50 @@ + + + + + + {% if info.is_none() %} + Not found - Comline + {% else %} + {{name}} - Comline + {% endif %} + +
+

Comline Project Indexer

+

Package manager server registry for packages

+
+ + +
+ {% if info.is_none() %} +

No project by the name {{name}} was found

+ {% else %} +

{{name}} - 0.0.1

+ {% endif %} +
+ + + diff --git a/package-manager-server/templates/packages.html b/package-manager-server/templates/packages.html new file mode 100644 index 0000000..804e9ca --- /dev/null +++ b/package-manager-server/templates/packages.html @@ -0,0 +1,39 @@ + + + + + Search Packages - Comline + +
+

Comline Project Indexer

+

Package manager server registry for packages

+
+ + +
+

Search for a package

+ +
+ + + diff --git a/package-manager/Cargo.toml b/package-manager/Cargo.toml new file mode 100644 index 0000000..3431581 --- /dev/null +++ b/package-manager/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "comline-package-manager" +version = "0.1.0" +edition = "2021" + + +[dependencies] +anyhow = "1.0.72" +clap = { version = "4.3.19", features = ["derive"] } +comline = {path = "../core/" } +once_cell = "1.18.0" +serde = { version = "1.0.177", features = ["derive"] } +shellexpand = "3.1.0" +toml = "0.7.6" diff --git a/package-manager/README.md b/package-manager/README.md new file mode 100644 index 0000000..a927618 --- /dev/null +++ b/package-manager/README.md @@ -0,0 +1,33 @@ +# Comline + +## Relevant resources +https://doc.rust-lang.org/cargo/reference/registries.html + + + +## Development Temporary Requirements +### Configuration Env Variable +This is temporary, the environment variables can be set manually but optimally should be done +on installation of the package manager + +### Linux +From this directory(`comline-rs/`, be sure you are here) in console, make an alias to the executable: + - Fish: `set COMLINE_HOME=~/.config/comline"` + - Bash: `COMLINE_HOME=~/.config/comline` (might be wrong, didn't test) + +You can check if its working by doing `echo $COMLINE_HOME` + + + +### Package Manager Alias +This is a very temporary alias for development (which might just easily break), since we need +to use the package manager sometimes, lets do a alias: + +### Linux +From this directory(`comline-rs/`, be sure you are here) in console, make an alias to the executable: + - Fish: `alias comline-dev="\"$(pwd)/target/debug/comline-package-manager\""` + - Bash: `alias comline-dev="\"$PWD/target/debug/comline-package-manager\""` (might be wrong, didn't test) + +Now the command `comline-dev` is available anywhere + + diff --git a/package-manager/src/client/mod.rs b/package-manager/src/client/mod.rs new file mode 100644 index 0000000..5086d00 --- /dev/null +++ b/package-manager/src/client/mod.rs @@ -0,0 +1,9 @@ +// Relative Modules +pub mod publish; + +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/package-manager/src/client/publish.rs b/package-manager/src/client/publish.rs new file mode 100644 index 0000000..ab305f0 --- /dev/null +++ b/package-manager/src/client/publish.rs @@ -0,0 +1,78 @@ +// Standard Uses +use std::path::Path; +use std::collections::HashMap; +use std::string::ToString; + +// Crate Uses + +// External Uses +use serde::{Deserialize, Serialize}; +use anyhow::Result; +use once_cell::sync::Lazy; + + +static COMLINE_CONFIGS_ENV_VAR: Lazy = Lazy::new(|| + shellexpand::tilde(&shellexpand::env("$COMLINE_HOME").unwrap()).to_string() +); + +static COMLINE_CONFIGS_PATH: Lazy<&Path> = Lazy::new(|| + Path::new(&*COMLINE_CONFIGS_ENV_VAR) +); + +static CREDENTIALS_PATH: Lazy = Lazy::new(|| + format!("{}/credentials.toml", COMLINE_CONFIGS_PATH.to_str().unwrap()) +); + + +#[derive(Serialize, Deserialize)] +pub struct Config { + credentials: HashMap +} + +#[derive(Serialize, Deserialize)] +pub struct Credential { + token: String +} + +pub fn login_to_registry(registry: String, token: String) -> Result<()> { + if !COMLINE_CONFIGS_PATH.exists() { + std::fs::create_dir_all(&*COMLINE_CONFIGS_PATH).unwrap(); + }; + + let creds_path = Path::new(&*CREDENTIALS_PATH); + + let mut config = if !creds_path.exists() { + let cfg = Config { + credentials: HashMap::from([ + (registry.clone(), Credential { token: token.clone() }) + ]) + }; + cfg + } else { + let contents = std::fs::read_to_string(creds_path)?; + toml::from_str::(&contents)? + }; + + let entry = config.credentials.entry(registry) + .or_insert(Credential { token }); + + // TODO: Attempt to login to the registry + + let result = toml::to_string(&config)?; + + std::fs::write(&creds_path, result).unwrap(); + + Ok(()) +} + +#[allow(unused)] +pub fn logout_from_registry(registry: String) { + +} + +#[allow(unused)] +pub fn publish_to_registries(registries: Vec) { + + todo!() +} + diff --git a/package-manager/src/main.rs b/package-manager/src/main.rs new file mode 100644 index 0000000..6f92b4c --- /dev/null +++ b/package-manager/src/main.rs @@ -0,0 +1,65 @@ +// Relative Modules +pub mod client; +pub mod manager; +pub mod tests; + +// Standard Uses + +// Crate Uses + +// External Uses +use clap::{Parser, Subcommand}; + + +#[derive(Parser)] +#[command(name = "Comline Package Manager")] +#[command(author = "Comline")] +#[command(version = "0.0.1")] +#[command(about = "Project manager and publisher tool for comline", long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + + +#[derive(Subcommand)] +enum Commands { + /// Create new congregation + New { name: String }, + + /// Add dependency to congregation + Add { dependency: String }, + + /// Build congregation + Build, + + /// Publish a congregation + Publish { + /// The registries on where to publish, divided by space + #[arg(long)] + registries: String, + } +} + + +pub fn main() { + let cli = Cli::parse(); + + use Commands::*; + match &cli.command { + New { name } => { + println!("{}", name); + }, + Build => { + todo!(); + }, + Publish { registries } => { + println!("Starting the publish process"); + let parts: Vec = registries.split_whitespace() + .map(|p| p.to_owned()).collect(); + + client::publish::publish_to_registries(parts); + }, + _ => unimplemented!("Command not implemented") + } +} diff --git a/package-manager/src/manager/dependencies.rs b/package-manager/src/manager/dependencies.rs new file mode 100644 index 0000000..6a74aec --- /dev/null +++ b/package-manager/src/manager/dependencies.rs @@ -0,0 +1,26 @@ +// Standard Uses +use std::path::Path; + +// Crate Uses +use comline::project::Project; +use comline::project::idl::ast::Dependency; + +// External Uses +use anyhow::Result; + + +#[allow(unused)] +pub fn fetch_dependencies(project: &Project, cache_path: &Path) -> Result<()> { + for dependency in project.dependencies() { + let dep = fetch_dependency(dependency)?; + } + + Ok(()) +} + +#[allow(unused)] +pub fn fetch_dependency(dependency: &Dependency) -> Result<()> { + + todo!() +} + diff --git a/package-manager/src/manager/indexer.rs b/package-manager/src/manager/indexer.rs new file mode 100644 index 0000000..01ee861 --- /dev/null +++ b/package-manager/src/manager/indexer.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/package-manager/src/manager/mod.rs b/package-manager/src/manager/mod.rs new file mode 100644 index 0000000..10a57aa --- /dev/null +++ b/package-manager/src/manager/mod.rs @@ -0,0 +1,11 @@ +// Relative Modules +mod dependencies; +mod indexer; + +// Standard Uses + +// Crate Uses + +// External Uses + + diff --git a/package-manager/src/tests/client/mod.rs b/package-manager/src/tests/client/mod.rs new file mode 100644 index 0000000..4b443c2 --- /dev/null +++ b/package-manager/src/tests/client/mod.rs @@ -0,0 +1,43 @@ +// Standard Uses + +// Crate Uses + +// External Uses +use crate::client::publish; + + +const COMLINE_HOME_NAME: &str = "COMLINE_HOME"; +const COMLINE_HOME_PATH: &str = "~/.config/comline"; + +#[test] +fn set_and_check_home_env_var() { + std::env::set_var(COMLINE_HOME_NAME, COMLINE_HOME_PATH); + assert_eq!(std::env::var(COMLINE_HOME_NAME), Ok(COMLINE_HOME_PATH.to_string())); +} + +#[test] +fn registry_login_default() { + set_and_check_home_env_var(); + + publish::login_to_registry( + "default".to_owned(), "TOKEN".to_owned() + ).unwrap(); +} + +#[test] +fn registry_login_test() { + set_and_check_home_env_var(); + + publish::login_to_registry( + "test".to_owned(), "TEST_TOKEN".to_owned() + ).unwrap(); +} + +#[test] +fn publish_into_registries() { + set_and_check_home_env_var(); + + registry_login_test(); + + publish::publish_to_registries(vec!["test".to_owned()]) +} diff --git a/package-manager/src/tests/mod.rs b/package-manager/src/tests/mod.rs new file mode 100644 index 0000000..b9babe5 --- /dev/null +++ b/package-manager/src/tests/mod.rs @@ -0,0 +1 @@ +pub mod client; diff --git a/proposals/enum_variant_associated_kind.ids b/proposals/enum_variant_associated_kind.ids new file mode 100644 index 0000000..e69de29 diff --git a/proposals/protocol_function_blanket_impl.ids b/proposals/protocol_function_blanket_impl.ids new file mode 100644 index 0000000..e69de29 diff --git a/proposals/protocol_property_discoverable.ids b/proposals/protocol_property_discoverable.ids new file mode 100644 index 0000000..2eb2cf9 --- /dev/null +++ b/proposals/protocol_property_discoverable.ids @@ -0,0 +1,6 @@ + +// TODO: Think what »discoverable« should mean, just like Fuchsia IDL +@discoverable +protocol OpenWorld { +} + diff --git a/proposals/protocol_provider_and_provenance.ids b/proposals/protocol_provider_and_provenance.ids new file mode 100644 index 0000000..acd54a3 --- /dev/null +++ b/proposals/protocol_provider_and_provenance.ids @@ -0,0 +1,9 @@ + +//#provider=Server +#provider=Multiple consumer=Multiple // TODO: These parameters need more situational introspection to be documented +protocol Feed { + // Fetches new articles since last fetch + @flexible async stream + #timeout=1000ms per_send_timeout=10s cooldown=10s + 1# function fetch_articles() -> Article[SENT_ARTICLES_MAX] +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml new file mode 100644 index 0000000..0ab0f3f --- /dev/null +++ b/runtime/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "comline-runtime" +version = "0.1.0" +edition = "2021" +# license = "Parity OR Prosperity" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bytemuck = { version = "1.13.1", features = ["derive"] } +downcast-rs = "1.2.0" diff --git a/runtime/src/casy/mod.rs b/runtime/src/casy/mod.rs new file mode 100644 index 0000000..cd1080e --- /dev/null +++ b/runtime/src/casy/mod.rs @@ -0,0 +1,17 @@ +// Relative Modules +pub mod open_rpc; + +// Standard Uses + +// Crate Uses + +// External Uses + + +pub trait CallSystem { + fn on_receive_data(&mut self, data: Box<[u8]>); + fn on_send_data(&mut self, data: Box<[u8]>); + + fn on_received_data(&self, data: &Box<[u8]>); + fn on_sent_data(&self, data: &[u8]); +} diff --git a/runtime/src/casy/open_rpc.rs b/runtime/src/casy/open_rpc.rs new file mode 100644 index 0000000..01ee861 --- /dev/null +++ b/runtime/src/casy/open_rpc.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs new file mode 100644 index 0000000..02c8dd2 --- /dev/null +++ b/runtime/src/handler.rs @@ -0,0 +1,10 @@ +// Standard Uses + +// Crate Uses + +// External Uses + + +pub trait Handler { + +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs new file mode 100644 index 0000000..c697371 --- /dev/null +++ b/runtime/src/lib.rs @@ -0,0 +1,5 @@ +// Relative Modules +pub mod prelude; +pub mod casy; +pub mod handler; +pub mod wire_format; diff --git a/runtime/src/prelude.rs b/runtime/src/prelude.rs new file mode 100644 index 0000000..01ee861 --- /dev/null +++ b/runtime/src/prelude.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/runtime/src/wire_format/mod.rs b/runtime/src/wire_format/mod.rs new file mode 100644 index 0000000..cdfbde4 --- /dev/null +++ b/runtime/src/wire_format/mod.rs @@ -0,0 +1,2 @@ +// Relative Modules +mod msgpack; diff --git a/runtime/src/wire_format/msgpack.rs b/runtime/src/wire_format/msgpack.rs new file mode 100644 index 0000000..01ee861 --- /dev/null +++ b/runtime/src/wire_format/msgpack.rs @@ -0,0 +1,6 @@ +// Standard Uses + +// Crate Uses + +// External Uses + diff --git a/runtime/tests/casy/mod.rs b/runtime/tests/casy/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/runtime/tests/mod.rs b/runtime/tests/mod.rs new file mode 100644 index 0000000..73a527b --- /dev/null +++ b/runtime/tests/mod.rs @@ -0,0 +1 @@ +mod casy; \ No newline at end of file diff --git a/src/codegen.rs b/src/codegen.rs deleted file mode 100644 index 142d5f4..0000000 --- a/src/codegen.rs +++ /dev/null @@ -1 +0,0 @@ -mod rust; \ No newline at end of file diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/codegen/rust.rs b/src/codegen/rust.rs deleted file mode 100644 index d15f38a..0000000 --- a/src/codegen/rust.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Standard Uses - -// Local Uses -//use crate::ir::unit::{Protocol, Unit}; - -// External Uses - -/* -fn unit_to_code(unit: Unit) -> String { - let mut code = String::new(); - - match unit { - Unit::Items(_) => {} - Unit::Tag(_) => {} - Unit::Namespace(_) => {} - Unit::Imports(_) => {} - Unit::Settings(_) => {} - Unit::Consts(_) => {} - Unit::Structs(_) => {} - Unit::Enums(_) => {} - Unit::Errors(_) => {} - Unit::Protocols(protocols) => { - for protocol in protocols { - code.push_str(&*protocol_to_code(protocol)) - } - } - } - - code -} -fn protocol_to_code(protocol: Protocol) -> String { - let mut code = String::new(); - - // Trait Start - // TODO: Maybe only include async_trait if at least one of the functions need it - code.push_str("#[async_trait]"); - code.push_str(&*format!("pub trait {} {{", protocol.id)); - - // Functions - for function in protocol.functions { - let mut fn_code = String::new(); - - // Async definition (with async_trait) - if function.async_ { - fn_code.push_str("async "); - } - - // Fn - fn_code.push_str(&*format!("fn {} (", function.id)); - - // Arguments - let mut idx = 0; - while idx != function.arguments.len() { - let argument = function.arguments.get(idx).unwrap(); - - if argument.id.is_none() { - fn_code.push_str(&*format!("arg{}: {:?}", idx, argument.type_)); - if idx != function.arguments.len() { fn_code.push_str(", ") } - } else { - fn_code.push_str(&*format!("{}: {:?}", argument.id.clone().unwrap(), argument.type_)); - if idx != function.arguments.len() { fn_code.push_str(", ") } - } - - idx += 1; - } - - // Returns - - code.push_str(&*fn_code) - } - - // Trait End - code.push_str("}}"); - - code.to_owned() -} -*/ diff --git a/src/idl/parser.rs b/src/idl/parser.rs deleted file mode 100644 index 09b71b7..0000000 --- a/src/idl/parser.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::new_unit::{Unit, UnitIndex, VUnit, WholeUnit}; -use crate::ir::primitive; -use crate::ir::primitive::TypeValue; - -// External Uses -use anyhow::Result; -use pest::{iterators::Pair, Parser}; - - -#[derive(Parser)] -#[grammar = "idl/idl.pest"] -pub struct IDLParser; - - -pub fn from_path(path: &str) -> Result { - let raw = std::fs::read_to_string(path).unwrap(); - let vunit = parse_into_unit(raw.clone().as_str()).unwrap(); - let index = UnitIndex::Index { path: path.to_string(), source: raw }; - - Ok((index, vunit)) -} - -pub fn parse_into_unit(content: &str) -> Result { - let pairs = IDLParser::parse(Rule::schema, content)?; - let mut unit = vec![]; - - for pair in pairs { unit.push(parse_inner(pair).unwrap()) } - - Ok(unit) -} - - -#[allow(unused)] -pub fn parse_inner(pairs: Pair) -> Result { - match pairs.as_rule() { - /* - Rule::schema => { - todo!() - }, - */ - Rule::namespace => { - Ok(Unit::Namespace(pairs.into_inner().as_str().to_owned())) - }, - Rule::import => { - Ok(Unit::Import(pairs.into_inner().as_str().to_owned())) - } - Rule::constant => { - let pairs = pairs.into_inner(); - - let mut docstring: Option = None; - let mut id: Option = None; - let mut kind: Option = None; - let mut default_value: Option = None; - - for pair in pairs { - match pair.as_rule() { - Rule::docstring => docstring = Some(pair.as_str().to_owned()), - Rule::id => id = Some(pair.as_str().to_owned()), - Rule::kind => kind = Some(primitive::to_type(pair.as_str())), - Rule::value => default_value = to_value_other(pair), - (r) => panic!("Rule not implemented on 'constant': {:?}", r) - } - } - - Ok(Unit::Constant { - docstring, - name: id.ok_or("Id is not present").unwrap(), - kind: kind.ok_or("Type is not present").unwrap(), - default_value, - }) - }, - Rule::settings => { - let mut pairs = pairs.into_inner(); - - let mut docstring: Option = None; - let mut name: Option = None; - let mut parameters: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::docstring => docstring = Some(pair.as_str().to_owned()), - Rule::id => name = Some(pair.as_str().to_owned()), - Rule::parameter => parameters.push(to_parameter(pair)), - (r) => panic!("Rule not implemented on 'settings': {:?}", r) - } - } - - Ok(Unit::Settings { - docstring, - name: name.unwrap(), - parameters, - }) - }, - Rule::enumeration => { - let mut pairs = pairs.into_inner(); - - let mut docstring: Option = None; - let mut name: Option = None; - let mut variants: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::docstring => docstring = Some(pair.as_str().to_owned()), - Rule::id => name = Some(pair.as_str().to_owned()), - Rule::enum_variant => { - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let kind = inner.next().map(|s| s.as_str().to_string()); - - variants.push(Unit::EnumVariant { - name, kind, - }); - }, - (r) => panic!("Rule not implemented on 'enumeration': {:?}", r) - } - } - - Ok(Unit::Enum { - docstring, name: name.unwrap(), - variants, - }) - }, - Rule::structure => { - let mut pairs = pairs.into_inner(); - - let mut docstring: Option = None; - let mut parameters: Vec = vec![]; - let mut name: Option = None; - let mut fields: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::docstring => docstring = Some(pair.as_str().to_owned()), - Rule::id => name = Some(pair.as_str().to_owned()), - Rule::parameter => parameters.push(to_parameter(pair)), - Rule::field => fields.push(to_field(pair)), - (r) => panic!("Rule not implemented on 'structure': {:?}", r) - } - } - - Ok(Unit::Struct { - docstring, parameters, - name: name.unwrap(), fields: vec![], - }) - } - (r) => panic!("Rule not implemented: {:?}", r) - } -} - -/* -pub fn to_parameters(mut pairs: Pairs) -> Vec { - let mut params = vec![]; - - let t = pairs.as_str(); - while let Some(pair) = pairs.next() { - let temp = pair.as_str(); - - params.push(to_parameter(pair.into_inner())); - } - - params -} -*/ - -pub fn to_parameter(mut pair: Pair) -> Unit { - let mut pairs = pair.into_inner(); - - Unit::Parameter { - name: pairs.next().unwrap().as_str().to_string(), - default_value: pairs.next().unwrap().as_str().to_string() - } -} - - -pub fn to_value_other(pair: Pair) -> Option { - let inner = pair.into_inner().next().unwrap(); - - match inner.as_rule() { - Rule::string => Some(inner.as_str().to_string()), - Rule::string_interpolated => Some(inner.as_str().to_string()), - Rule::digit => Some(inner.as_str().to_string()), - r => panic!("Rule not implemented in 'value': {:?}", r) - } - - // "".to_string() -} - -pub fn to_field(pair: Pair) -> Unit { - let mut pairs = pair.into_inner(); - - let mut docstring: Option = None; - let mut parameters: Vec = vec![]; - let mut optional: bool = false; - let mut name: Option = None; - let mut kind: Option = None; - let mut default_value: Option = None; - - for pair in pairs { - match pair.as_rule() { - Rule::docstring => docstring = Some(pair.as_str().to_owned()), - Rule::parameter => parameters.push(to_parameter(pair)), - Rule::requirement => optional = true, - Rule::id => name = Some(pair.as_str().to_owned()), - Rule::kind => kind = Some(pair.as_str().to_owned()), - Rule::value => default_value = Some(pair.as_str().to_owned()), - r => panic!("Rule not implemented in 'field': {:?}", r) - } - } - - Unit::Field { - docstring, parameters, - optional, - name: name.unwrap(), kind: kind.unwrap(), default_value, - } -} - diff --git a/src/idl/pest.rs b/src/idl/pest.rs deleted file mode 100644 index a7b30fa..0000000 --- a/src/idl/pest.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::primitive; -use crate::ir::primitive::TypeValue; - -use crate::ir::unit::{ - Argument, Const, Direction, Enum, EnumVariant, - Error, Field, Function, Parameter, - Protocol, Settings, Struct, Unit -}; - -// External Uses -use anyhow::Result; -use pest::iterators::{Pair, Pairs}; -use pest::Parser; - - -#[derive(Parser)] -#[grammar = "idl/idl.pest"] -pub struct IDLParser; - -/* -pub fn parse_into_unit(raw: &str) -> Result { - let parse = IDLParser::parse(Rule::schema, raw)?; - - let mut items: Vec = vec![]; - - for record in parse.into_iter() { - items.push(parse_inner(record).unwrap()); - } - - Ok(Unit::Items(items)) -} - -fn parse_inner(record: Pair) -> Result { - // let consts: Vec = vec![]; - - let inner = match record.as_rule() { - Rule::namespace => { - Ok(Unit::Namespace(record.into_inner().as_str().to_owned())) - } - Rule::constant => { - let pairs = record.into_inner(); - - let mut id: Option = None; - let mut kind: Option = None; - let mut default: Option> = None; - - for pair in pairs.flatten().collect::>>() { - match pair.as_rule() { - Rule::id => id = Some(pair.as_str().to_owned()), - Rule::kind => kind = Some(primitive::to_type(pair.as_str())), - Rule::value => { - let under = pair.into_inner().next().unwrap(); - - default = match under.as_rule() { - Rule::inner_string => Some(under.as_str().as_bytes().to_vec()), - Rule::inner_namespaced => Some(under.as_str().as_bytes().to_vec()), - _ => None - }; - } - Rule::inner_string | Rule::inner_namespaced => {} - _ => { unreachable!("Reached a unexpected rule that was possibly forgotten") } - } - } - - Ok( - Unit::Consts( - vec![ - Const { - id: id.ok_or("Id is not present").unwrap(), - type_: kind.ok_or("Type is not present").unwrap(), - default_value: default.unwrap() - } - ] - ) - ) - } - Rule::settings => { - let mut pairs = record.into_inner(); - - let id: String = pairs.next().unwrap().as_str().to_owned(); - let parameters: Vec = to_parameters(pairs); - - Ok(Unit::Settings( - vec![ - Settings { id, parameters } - ] - )) - } - Rule::enumeration => { - let mut pairs = record.into_inner(); - - // let mut index: Option = None; - let id: String = pairs.next().unwrap().as_str().to_owned(); - let variants: Vec = vec![]; - - Ok(Unit::Enums( - vec![ - Enum { id, variants } - ] - )) - } - Rule::enum_variant => { todo!() } - Rule::structure => { - let mut pairs = record.into_inner(); - - let id: String = pairs.next().unwrap().as_str().to_owned(); - let mut parameters: Vec = vec![]; - let mut fields: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::parameter => parameters.push(to_parameter(pair.into_inner())), - Rule::field => fields.push(to_field(pair.into_inner())), - _ => unreachable!() - } - } - - Ok(Unit::Structs(vec![ - Struct { id, parameters, fields } - ])) - } - Rule::error => { - let mut pairs = record.into_inner(); - - let id: String = pairs.next().unwrap().as_str().to_owned(); - let mut parameters: Vec = vec![]; - let mut fields: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::parameter => parameters.push(to_parameter(pair.into_inner())), - Rule::field => fields.push(to_field(pair.into_inner())), - _ => unreachable!() - } - } - - Ok(Unit::Errors(vec![ - Error { id, parameters, fields } - ])) - } - Rule::protocol => { - let mut pairs = record.into_inner(); - - let id: String = pairs.next().unwrap().as_str().to_owned(); - let mut parameters: Vec = vec![]; - let mut functions: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::parameter => parameters.push(to_parameter(pair.into_inner())), - Rule::function => functions.push(to_function(pair.into_inner())), - _ => unreachable!() - } - } - - Ok(Unit::Protocols(vec![ - Protocol { - id, - parameters, - functions: vec![], - } - ])) - } - Rule::function => { todo!() } - Rule::argument => { todo!() } - Rule::returns => { todo!() } - Rule::throw => { todo!() } - Rule::COMMENT => { todo!() } - _ => unreachable!() - }; - - inner -} -*/ - -pub fn to_parameters(mut pairs: Pairs) -> Vec { - let mut params = vec![]; - - while let Some(pair) = pairs.next() { - params.push(to_parameter(pair.into_inner())); - } - - params -} - -pub fn to_parameter(mut pairs: Pairs) -> Parameter { - Parameter { - id: pairs.next().unwrap().as_str().to_owned(), - value: pairs.next().unwrap().as_str().as_bytes().to_vec() - } -} - -fn to_field(pairs: Pairs) -> Field { - let mut index: u8 = 0; - let mut optional: bool = false; - let mut field_id: Option = None; - let mut field_kind: Option = None; - let mut field_value: Option> = None; - - for field_pair in pairs { - match field_pair.as_rule() { - Rule::index => index = field_pair.as_str().parse().unwrap(), - Rule::requirement => optional = true, - Rule::id => field_id = Some(field_pair.as_str().to_owned()), - Rule::kind => field_kind = Some(primitive::to_type(field_pair.as_str())), - Rule::value => field_value = Some(field_pair.as_str().as_bytes().to_vec()), - _ => println!("field bad {:?}", field_pair.as_rule()) - } - } - - Field { - id: field_id.ok_or("Id is not present").unwrap(), - index, optional, - type_: field_kind.unwrap(), - default_value: field_value.unwrap(), - } -} - -fn to_function(pairs: Pairs) -> Function { - let mut index: Option = None; - let mut async_: bool = false; - let mut direction = Direction::Both; - let mut id: Option = None; - let mut arguments: Vec = vec![]; - let parameters: Vec = vec![]; - let returns: Vec = vec![]; - let throws: Vec = vec![]; - - for pair in pairs { - match pair.as_rule() { - Rule::index => index = Some(pair.as_str().parse().unwrap()), - Rule::id => id = Some(pair.as_str().to_owned()), - Rule::direction => direction = match pair.as_str() { - "client" => Direction::Client, - "server" => Direction::Server, - dir => panic!("Direction {:#} does not exist", dir) - }, - Rule::asynchronous => async_ = true, - Rule::argument => { - arguments.push(to_argument(pair.into_inner())) - } - Rule::returns => {} - Rule::parameter => {} - Rule::throw => {} - _ => unreachable!() - } - } - - Function { - index: index.unwrap(), - id: id.unwrap(), - async_, - direction, - arguments, return_: returns, parameters, throws, - } -} - -fn to_argument(pairs: Pairs) -> Argument { - let mut id: Option = None; - let mut kind: Option = None; - - for pair in pairs { - match pair.as_rule() { - Rule::id => id = Some(pair.as_str().to_owned()), - Rule::kind => kind = Some(primitive::to_type(pair.as_str())), - _ => unreachable!() - } - } - - Argument { id, type_: kind.unwrap() } -} - diff --git a/src/ir.rs b/src/ir.rs deleted file mode 100644 index 6d15aa2..0000000 --- a/src/ir.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Relative Modules -mod sanitizer; -pub mod context; -mod serialization; -pub mod unit; -pub mod new_unit; -pub mod primitive; -pub mod compiler; diff --git a/src/ir/compiler.rs b/src/ir/compiler.rs deleted file mode 100644 index 98c929a..0000000 --- a/src/ir/compiler.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::context::Context; -use crate::ir::new_unit::{Unit, VUnit}; - -// External Uses -use anyhow::{Result, bail}; - - -pub fn interpret_unit(context: Context) -> Result { - let interpreted = vec![]; - - for unit in &context.main.1 { - match unit { - // Unit::Tag(_) => {} - Unit::Namespace(n) => {} - Unit::Import(i) => { - let relative_unit = context.find_unit(i)?; - - bail!(format!("Could not find in namespace {}", i)) - } - /* - Unit::Constant { .. } => {} - Unit::Enum { .. } => {} - Unit::EnumVariant { .. } => {} - Unit::Property { .. } => {} - Unit::Parameter { .. } => {} - Unit::Settings { .. } => {} - Unit::Struct { .. } => {} - Unit::Protocol { .. } => {} - Unit::Function { .. } => {} - Unit::Error { .. } => {} - Unit::Validator { .. } => {} - Unit::Field { .. } => {} - */ - r => panic!("Left to impl: {:?}", r) - } - } - - Ok(interpreted) -} - diff --git a/src/ir/context.rs b/src/ir/context.rs deleted file mode 100644 index b16bf50..0000000 --- a/src/ir/context.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::unit::Unit; -use crate::ir::serialization::Origin; -use crate::ir::new_unit::{UnitIndex, VUnit, WholeUnit}; - -// External Uses -use anyhow::{Result, bail}; - - -pub struct Context { - pub main: WholeUnit, - pub relative_units: Vec -} - -#[allow(unused)] -impl Context { - pub fn with_main(main: WholeUnit) -> Self{ - Context { main, relative_units: vec![] } - } - - pub(crate) fn add_relative_unit(mut self, whole_unit: WholeUnit) { - self.relative_units.push(whole_unit) - } - - pub(crate) fn sanitize_units(self) { - todo!() - } - - pub(crate) fn find_unit(&self, import: &str) -> Result<&WholeUnit> { - - todo!() - } -} - diff --git a/src/ir/new_unit.rs b/src/ir/new_unit.rs deleted file mode 100644 index 6347bd0..0000000 --- a/src/ir/new_unit.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::primitive::TypeValue; - -// External Uses - - -pub type OrderIndex = u16; - -#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum Direction { Client, Server, Both } - - -#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] -pub enum Unit { - /* - Schema { - tag: String, - namespace: String, - imports: Vec, - constants: Vec, - settings: Vec, - enums: Vec, - structures: Vec, - validators: Vec, - errors: Vec, - protocols: Vec - }, - */ - Tag(String), - Namespace(String), - Import(String), - Constant { - docstring: Option, - name: String, - kind: TypeValue, // String - default_value: Option, - }, - Enum { - docstring: Option, - name: String, - variants: Vec - }, - EnumVariant { - name: String, - kind: Option - }, - Property { - name: String, - values: Vec - }, - Parameter { - name: String, - default_value: String - }, - Settings { - docstring: Option, - name: String, - parameters: Vec, - }, - Struct { - docstring: Option, - parameters: Vec, - name: String, - fields: Vec, - }, - Protocol { - docstring: String, - parameters: Vec, - name: String, - functions: Vec - }, - Function { - docstring: String, - // index: OrderIndex, - name: String, - synchronous: bool, - direction: Direction, - arguments: Vec, - returns: Vec, - throws: Vec - }, - Error { - docstring: String, - parameters: Vec, - name: String, - fields: Vec - }, - Validator { - docstring: String, - parameters: Vec, - name: String, - message: String, - }, - Field { - docstring: Option, - parameters: Vec, - // index: OrderIndex, - optional: bool, - name: String, - kind: String, - default_value: Option, - } -} - -#[derive(PartialEq, Debug)] -pub enum UnitIndex { - Index { - path: String, - source: String, - // nodes: Vec - }, - Node { - index: u32, - start_position: u32, length: u32 - } -} - -pub type VUnit = Vec; -pub type WholeUnit = (UnitIndex, VUnit); diff --git a/src/ir/primitive.rs b/src/ir/primitive.rs deleted file mode 100644 index b7dadf8..0000000 --- a/src/ir/primitive.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Standard Uses - -// Local Uses - -// External Uses - -#[derive(Debug, Eq, PartialEq)] -#[repr(u8)] -pub enum Primitive { - U8(u8) = 0, - U16(u16), - U32(u32), - U64(u64), - U128(u128), - - S8(u8), - S16(u16), - S32(u32), - S64(u64), - S128(u128), - - // Float(f32), - // Double(f64), - - String(String), - Namespaced(String) -} - -#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] -#[repr(u8)] -pub enum TypeValue { - U8 = 0, U16, U32, U64, U128, - S8, S16, S32, S64, S128, - // Float, Double, - String, - Namespaced -} - -fn to_primitive(type_: &str, value: &str) -> Primitive { - match type_ { - "u8" => Primitive::U8(value.parse().unwrap()), - "u16" => Primitive::U16(value.parse().unwrap()), - "u32" => Primitive::U32(value.parse().unwrap()), - "u64" => Primitive::U64(value.parse().unwrap()), - "u128" => Primitive::U128(value.parse().unwrap()), - - "s8" => Primitive::U8(value.parse().unwrap()), - "s16" => Primitive::U16(value.parse().unwrap()), - "s32" => Primitive::U32(value.parse().unwrap()), - "s64" => Primitive::U64(value.parse().unwrap()), - "s128" => Primitive::U128(value.parse().unwrap()), - - // TODO: Should multiple alias be present? »string« could be enough - "str" | "string" => Primitive::String(value.to_owned()), - _ => { panic!("Got unknown type '{type_}'") } - } -} - -pub fn to_type(type_: &str) -> TypeValue { - match type_ { - "u8" => TypeValue::U8{}, - "u16" => TypeValue::U16{}, - "u32" => TypeValue::U32{}, - "u64" => TypeValue::U64{}, - "u128" => TypeValue::U128{}, - - "s8" => TypeValue::S8{}, - "s16" => TypeValue::S16{}, - "s32" => TypeValue::S32{}, - "s64" => TypeValue::S64{}, - "s128" => TypeValue::S128{}, - - "str" => TypeValue::String{}, - - _ => TypeValue::Namespaced{} - } -} - -fn to_value(type_: &TypeValue, value: &str) -> Vec { - match type_ { - TypeValue::U8 => Vec::from([value.parse::().unwrap()]), - TypeValue::U16 => Vec::from([value.parse::().unwrap()]), - TypeValue::U32 => Vec::from([value.parse::().unwrap()]), - TypeValue::U64 => Vec::from([value.parse::().unwrap()]), - TypeValue::U128 => Vec::from([value.parse::().unwrap()]), - TypeValue::S8 => Vec::from([value.parse::().unwrap()]), - TypeValue::S16 => Vec::from([value.parse::().unwrap()]), - TypeValue::S32 => Vec::from([value.parse::().unwrap()]), - TypeValue::S64 => Vec::from([value.parse::().unwrap()]), - TypeValue::S128 => Vec::from([value.parse::().unwrap()]), - TypeValue::String => Vec::from([value.parse::().unwrap()]), - TypeValue::Namespaced => Vec::from([value.parse::().unwrap()]), - } -} diff --git a/src/project/idl/ast.rs b/src/project/idl/ast.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/project/idl/idc.pest b/src/project/idl/idc.pest new file mode 100644 index 0000000..6b0ea64 --- /dev/null +++ b/src/project/idl/idc.pest @@ -0,0 +1,7 @@ +// +// Created by intellij-pest on 2023-07-07 +// idc +// Author: nibunta +// + +idc = { "Hello World!" } diff --git a/src/project/idl/mod.rs b/src/project/idl/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/project/idl/parser.rs b/src/project/idl/parser.rs new file mode 100644 index 0000000..db5ccfe --- /dev/null +++ b/src/project/idl/parser.rs @@ -0,0 +1,16 @@ +// Standard Uses + +// Local Uses + +// External Uses +use anyhow::Result; +use pest::{iterators::Pair, Parser}; + +#[derive(Parser)] +#[grammar = "project/idl/idc.pest"] +pub struct IDLParser; + + +pub fn from_path() -> { + +} diff --git a/src/project/ir/mod.rs b/src/project/ir/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/project/mod.rs b/src/project/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/idl/ast/mod.rs b/src/schema/idl/ast/mod.rs new file mode 100644 index 0000000..8835a22 --- /dev/null +++ b/src/schema/idl/ast/mod.rs @@ -0,0 +1,3 @@ +// Relative Modules +pub mod unit; +pub mod old_unit; diff --git a/src/schema/idl/ast/old_unit.rs b/src/schema/idl/ast/old_unit.rs new file mode 100644 index 0000000..4bac37d --- /dev/null +++ b/src/schema/idl/ast/old_unit.rs @@ -0,0 +1,116 @@ +// Standard Uses + +// Local Uses + +// External Uses + +/* +/// Intermediate Representation Unit +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub enum Unit { + Items(Vec), + Tag(String), + Namespace(String), + Imports(String), + Settings(Vec), + Consts(Vec), + Structs(Vec), + Enums(Vec), + Errors(Vec), + Protocols(Vec) +} + + +#[derive(Debug, Eq, PartialEq)] +pub struct Settings { + pub id: String, + pub parameters: Vec, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct Const { + pub id: String, + pub type_: Kind, + pub default_value: Vec +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Struct { + pub id: String, + pub parameters: Vec, + pub fields: Vec, +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Enum { + pub id: String, + pub variants: Vec +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct EnumVariant { + pub id: String, + pub type_: Option +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Error { + pub id: String, + pub parameters: Vec, + pub fields: Vec, +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Parameter { + pub id: String, + pub value: Vec +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Field { + pub index: u8, + pub optional: bool, + pub id: String, + pub type_: Kind, + pub default_value: Vec, +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Protocol { + pub id: String, + pub parameters: Vec, + pub functions: Vec +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub enum Direction { Client, Server, Both } + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Function { + pub index: u8, + pub id: String, + pub async_: bool, + pub direction: Direction, + pub arguments: Vec, + pub return_: Vec, + pub parameters: Vec, + pub throws: Vec +} + +#[allow(unused)] +#[derive(Debug, Eq, PartialEq)] +pub struct Argument { + pub id: Option, + pub type_: Kind +} +*/ \ No newline at end of file diff --git a/src/schema/idl/ast/unit.rs b/src/schema/idl/ast/unit.rs new file mode 100644 index 0000000..6a59e30 --- /dev/null +++ b/src/schema/idl/ast/unit.rs @@ -0,0 +1,160 @@ +// Standard Uses + +// Local Uses + +// External Uses + + +pub type OrderIndex = u16; + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum Direction { Client, Server, Both } + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum ASTUnit { + /* + Schema { + tag: String, + namespace: String, + imports: Vec, + constants: Vec, + settings: Vec, + enums: Vec, + structures: Vec, + validators: Vec, + errors: Vec, + protocols: Vec + }, + */ + Tag(String), + Namespace(String), + Import(String), + Docstring { + variable: Option, + description: String + }, + Constant { + docstring: Vec, + name: String, + kind: String, + default_value: Option, + }, + Property { + name: String, + expression: Option + }, + Parameter { + name: String, + default_value: String + }, + ExpressionBlock { + function_calls: Vec + }, + // + Enum { + docstring: Vec, + name: String, + variants: Vec + }, + EnumVariant { + name: String, + kind: Option + }, + Settings { + docstring: Vec, + name: String, + parameters: Vec, + }, + Struct { + docstring: Vec, + parameters: Vec, + name: String, + fields: Vec, + }, + Protocol { + docstring: Vec, + parameters: Vec, + name: String, + functions: Vec + }, + Function { + docstring: Vec, + name: String, + synchronous: bool, + direction: Direction, + arguments: Vec, + returns: Vec, + throws: Vec + }, + Error { + docstring: Vec, + parameters: Vec, + name: String, + message: String, + fields: Vec + }, + Validator { + docstring: Vec, + properties: Vec, + name: String, + expression_block: Box + }, + Field { + docstring: Vec, + parameters: Vec, + // index: OrderIndex, + optional: bool, + name: String, + kind: String, + default_value: Option, + } +} + +pub(crate) fn namespace(vunit: &VUnit) -> &String { + let mut namespace: Option<&String> = None; + + for node in vunit { + match node { + ASTUnit::Namespace(n) => namespace = Some(n), + _ => {} + } + } + + namespace.unwrap() +} + +#[derive(PartialEq, Debug)] +pub enum UnitIndex { + Index { + path: String, + source: String, + // nodes: Vec + }, + Node { + index: u32, + start_position: u32, length: u32 + } +} + +#[derive(PartialEq, Debug)] +pub struct VIndex { + pub meta: UnitIndex, + pub nodes: Vec +} + +impl VIndex { + pub(crate) fn filename(&self) -> String { + match &self.meta { + UnitIndex::Index { path, .. } => path.clone(), + _ => panic!("Should not be here") + } + } + + pub(crate) fn provenance_details(&self) -> (String, String) { + todo!() + } +} + +pub type VUnit = Vec; +pub type WholeUnit = (VIndex, VUnit); diff --git a/src/idl/constants.rs b/src/schema/idl/constants.rs similarity index 73% rename from src/idl/constants.rs rename to src/schema/idl/constants.rs index 887e5d7..d7b35ab 100644 --- a/src/idl/constants.rs +++ b/src/schema/idl/constants.rs @@ -4,5 +4,7 @@ // External Uses + +pub const CONGREGATION_EXTENSION: &str = "idc"; pub const SCHEMA_EXTENSION: &str = "ids"; pub const UNIT_EXTENSION: &str = "idu"; diff --git a/src/idl/idl.pest b/src/schema/idl/idl.pest similarity index 67% rename from src/idl/idl.pest rename to src/schema/idl/idl.pest index b6fc049..2557a76 100644 --- a/src/idl/idl.pest +++ b/src/schema/idl/idl.pest @@ -27,45 +27,60 @@ constant = { } settings = { - WS? ~ "settings" ~ WS? ~ id? ~ WS? + WS? ~ docstring* + ~ WS? ~ "settings" ~ WS? ~ id? ~ WS? ~ "{" ~ WS? ~ parameter* ~ WS? ~ "}" } enumeration = { - WS? ~ property* + WS? ~ docstring* ~ property* ~ "enum" ~ WS ~ id ~ WS? ~ "{" ~ WS? ~ enum_variant+ ~ WS? ~ "}" ~ WS? } enum_variant = { - (index ~ "#")? ~ WS? ~ id ~ WS? + (index ~ "#")? ~ WS? + // TODO: Uncomment and replace the line below when this feature will be addressed + // ~ id ~ enum_variant_field? + ~ id + ~ WS? +} +enum_variant_field = { + "(" + ~ kind + ~ ")" } validator = { - WS? ~ property* + WS? ~ docstring* ~ property* ~ "validator" ~ WS ~ id ~ WS? - ~ "{" ~ WS? ~ function_call* ~ WS? ~ "}" + ~ "{" ~ WS? ~ expression_block ~ WS? ~ "}" ~ WS? } -// TODO: This is a temporary arragement of -// possibly used chars in a expression, -// a real comprehension needs to be done expression = { (operation ~ WS? ~ boolean_operator? ~ WS?)+ } +item = { + domain_namespaced | domain | variable +} function_call = { - id ~ WS? ~ "(" ~ WS? - ~ (","? ~ WS? ~ function_call_arg)* + item ~ WS? + ~ "(" ~ WS? + ~ function_call_arg* ~ WS? ~ ")" ~ WS? } -function_call_arg = { expression | string } +function_call_arg = { + WS? ~ ","? ~ WS? + ~ (operation | value) + ~ WS? +} -entity = { digit+ | domain } +entity = { number | variable } operation = { - (entity ~ WS? ~ operator ~ WS? - ~ (expression | entity)+) - + entity ~ WS? + ~ (boolean_operator | operator) + ~ WS? ~ (value | entity)+ } operator = { "==" | "!=" @@ -136,30 +151,37 @@ throw = { // Common Rules property = { - WS? ~ "@" ~ WS? ~ domain ~ WS? ~ "=" - ~ ( - domain_namespaced | domain - | number | property_array - ) - ~ (NEWLINE | WS)* + WS? ~ "@" + ~ WS? ~ property_domain + ~ WS? ~ "=" ~ WS? + ~ property_expression ~ WS? +} +property_domain = { + variable | domain +} +property_expression = { + (domain_namespaced | domain + | number | property_array) } property_array = { - "[" ~ property_instance* ~ "]" + "[" ~ WS? + ~ property_instance* + ~ WS? ~ "]" } property_instance = { - (NEWLINE | WS)* - ~ domain + WS? ~ domain ~ "(" ~ property_attribute* ~ ")" - ~ (NEWLINE | WS)* + ~ WS? } property_attribute = { - (NEWLINE | WS)* - ~ id ~ "=" ~ kind + WS? ~ id ~ "=" ~ kind } +expression_block = { function_call* } -domain = @{ (id | ".")+ } -domain_namespaced = @{ (alpha | "::" | "_")+ } +variable = @{ (id | kind | ".")+ } +domain = @{ (id | "::")+ } +domain_namespaced = @{ (id | "::" | "_")+ } number = @{ digit+ } id = @{ (alpha | "_")+ } kind = @{ (alpha | digit)+ } @@ -170,14 +192,24 @@ instantiation = { } docstring = { - "///" ~ (!NEWLINE ~ ANY)* ~ NEWLINE + "///" ~ + (docstring_property | docstring_description) + ~ NEWLINE +} +docstring_property = { + " "* ~ "@" ~ " "* ~ domain + ~ " "* ~ ":" + ~ " "? ~ docstring_description +} +docstring_description = @{ + (!NEWLINE ~ ANY)+ } value = { - "true" | "false" | digit + "true" | "false" | number | string | string_interpolated | instantiation - | domain | domain_namespaced + | variable | domain | domain_namespaced } string = { @@ -191,7 +223,9 @@ string_interpolated_inner = _{ } string_interpolation = _{ "{" ~ domain ~ "}" } -string_inner = _{ (string_interpolation | char)* } +string_inner = _{ + (string_interpolation | char)* +} char = { !("\"" | "\\") ~ ANY | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") diff --git a/src/idl.rs b/src/schema/idl/mod.rs similarity index 62% rename from src/idl.rs rename to src/schema/idl/mod.rs index e91bf23..bac385e 100644 --- a/src/idl.rs +++ b/src/schema/idl/mod.rs @@ -1,3 +1,3 @@ -pub mod pest; +// Relative Modules pub mod parser; pub mod constants; \ No newline at end of file diff --git a/src/schema/idl/parser.rs b/src/schema/idl/parser.rs new file mode 100644 index 0000000..2b427ca --- /dev/null +++ b/src/schema/idl/parser.rs @@ -0,0 +1,370 @@ +// Standard Uses + +// Local Uses +use crate::ir::ast::unit::{ASTUnit, UnitIndex, VIndex, VUnit, WholeUnit}; + +// External Uses +use anyhow::Result; +use pest::{iterators::Pair, Parser}; + + + +#[derive(Parser)] +#[grammar = "idl/idl.pest"] +pub struct IDLParser; + + +pub fn from_path(path: &str) -> Result { + let raw = std::fs::read_to_string(path).unwrap(); + let vunit = parse_into_unit(raw.clone().as_str()).unwrap(); + let vindex = VIndex { + meta: UnitIndex::Index { path: path.to_string(), source: raw }, + nodes: vec![] + }; + + Ok((vindex, vunit)) +} + +pub fn parse_into_unit(content: &str) -> Result { + let pairs = IDLParser::parse(Rule::schema, content)?; + let mut unit = vec![]; + + for pair in pairs { unit.push(parse_inner(pair).unwrap()) } + + Ok(unit) +} + + +#[allow(unused)] +pub fn parse_inner(pairs: Pair) -> Result { + match pairs.as_rule() { + Rule::namespace => { + Ok(ASTUnit::Namespace(pairs.into_inner().as_str().to_owned())) + }, + Rule::import => { + Ok(ASTUnit::Import(pairs.into_inner().as_str().to_owned())) + } + Rule::constant => { + let pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut id: Option = None; + let mut kind: Option = None; + let mut default_value: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => id = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(pair.as_str().to_owned()), + Rule::value => default_value = to_value_other(pair), + (r) => panic!("Rule not implemented on 'constant': {:?}", r) + } + } + + Ok(ASTUnit::Constant { + docstring: docstrings, + name: id.ok_or("Id is not present").unwrap(), + kind: kind.ok_or("Type is not present").unwrap(), + default_value, + }) + }, + Rule::settings => { + let mut pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut name: Option = None; + let mut parameters: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::parameter => parameters.push(to_parameter(pair)), + (r) => panic!("Rule not implemented on 'settings': {:?}", r) + } + } + + Ok(ASTUnit::Settings { + docstring: docstrings, + name: name.unwrap(), + parameters, + }) + }, + Rule::enumeration => { + let mut pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut name: Option = None; + let mut variants: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::enum_variant => { + let mut inner = pair.into_inner(); + let name = inner.next().unwrap().as_str().to_string(); + let kind = inner.next().map(|s| s.as_str().to_string()); + + variants.push(ASTUnit::EnumVariant { + name, kind, + }); + }, + (r) => panic!("Rule not implemented on 'enumeration': {:?}", r) + } + } + + Ok(ASTUnit::Enum { + docstring: docstrings, + name: name.unwrap(), variants, + }) + }, + Rule::structure => { + let mut pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut parameters: Vec = vec![]; + let mut name: Option = None; + let mut fields: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::parameter => parameters.push(to_parameter(pair)), + Rule::field => fields.push(to_field(pair)), + (r) => panic!("Rule not implemented on 'structure': {:?}", r) + } + } + + Ok(ASTUnit::Struct { + docstring: docstrings, parameters, + name: name.unwrap(), fields: vec![], + }) + }, + Rule::validator => { + let mut pairs = pairs.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut properties: Vec = vec![]; + let mut name: Option = None; + let mut expression_block: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::property => properties.push(to_property(pair)), + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::expression_block => + expression_block = Some(to_expression_block(pair)), + (r) => panic!("Rule not implemented on 'validator': {:?}", r) + } + } + + Ok(ASTUnit::Validator { + docstring: docstrings, properties, + name: name.unwrap(), expression_block: Box::from(expression_block.unwrap()), + }) + }, + r => panic!("Rule not implemented: {:?}", r) + } +} + +pub fn to_docstring(pair: Pair) -> ASTUnit { + let pair = pair.into_inner().next().unwrap(); + + match pair.as_rule() { + Rule::docstring_property => { + let pairs = pair.into_inner(); + + let mut variable: Option = None; + let mut description: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::domain => variable = Some(pair.as_str().to_owned()), + Rule::docstring_description => description = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented: {:?}", r) + } + } + + return ASTUnit::Docstring { + variable, description: description.unwrap(), + } + }, + Rule::docstring_description => { + return ASTUnit::Docstring { variable: None, description: pair.as_str().to_owned()} + }, + r => panic!("Rule not implemented: {:?}", r) + } +} + +/* +pub fn to_parameters(mut pairs: Pairs) -> Vec { + let mut params = vec![]; + + let t = pairs.as_str(); + while let Some(pair) = pairs.next() { + let temp = pair.as_str(); + + params.push(to_parameter(pair.into_inner())); + } + + params +} +*/ + +/* +pub fn to_parameters(mut pairs: Pairs) -> Vec { + let mut params = vec![]; + + while let Some(pair) = pairs.next() { + params.push(to_parameter(pair.into_inner())); + } + + params +} +*/ + + +pub fn to_parameter(pair: Pair) -> ASTUnit { + let mut pairs = pair.into_inner(); + + ASTUnit::Parameter { + name: pairs.next().unwrap().as_str().to_string(), + default_value: pairs.next().unwrap().as_str().to_string() + } +} + +pub fn to_value_other(pair: Pair) -> Option { + let inner = pair.into_inner().next().unwrap(); + + match inner.as_rule() { + Rule::string => Some(inner.as_str().to_string()), + Rule::string_interpolated => Some(inner.as_str().to_string()), + Rule::number => Some(inner.as_str().to_string()), + r => panic!("Rule not implemented in 'value': {:?}", r) + } + + // "".to_string() +} + +pub fn to_field(pair: Pair) -> ASTUnit { + let pairs = pair.into_inner(); + + let mut docstrings: Vec = vec![]; + let mut parameters: Vec = vec![]; + let mut optional: bool = false; + let mut name: Option = None; + let mut kind: Option = None; + let mut default_value: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::docstring => docstrings.push(to_docstring(pair)), + Rule::parameter => parameters.push(to_parameter(pair)), + Rule::requirement => optional = true, + Rule::id => name = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(pair.as_str().to_owned()), + Rule::value => default_value = Some(pair.as_str().to_owned()), + r => panic!("Rule not implemented in 'field': {:?}", r) + } + } + + ASTUnit::Field { + docstring: docstrings, parameters, + optional, name: name.unwrap(), kind: kind.unwrap(), default_value, + } +} + +pub fn to_property(pair: Pair) -> ASTUnit { + let inner = pair.into_inner().next().unwrap(); + + let mut name: Option = None; + let mut expression: Option = None; + + match inner.as_rule() { + Rule::property_domain => name = Some(inner.as_str().to_string()), + Rule::property_expression => expression = Some(inner.as_str().to_string()), + r => panic!("Rule not implemented in 'property': {:?}", r) + } + + ASTUnit::Property { name: name.unwrap(), expression } +} + +/* +fn to_function(pairs: Pairs) -> Function { + let mut index: Option = None; + let mut async_: bool = false; + let mut direction = Direction::Both; + let mut id: Option = None; + let mut arguments: Vec = vec![]; + let parameters: Vec = vec![]; + let returns: Vec = vec![]; + let throws: Vec = vec![]; + + for pair in pairs { + match pair.as_rule() { + Rule::index => index = Some(pair.as_str().parse().unwrap()), + Rule::id => id = Some(pair.as_str().to_owned()), + Rule::direction => direction = match pair.as_str() { + "client" => Direction::Client, + "server" => Direction::Server, + dir => panic!("Direction {:#} does not exist", dir) + }, + Rule::asynchronous => async_ = true, + Rule::argument => { + arguments.push(to_argument(pair.into_inner())) + } + Rule::returns => {} + Rule::parameter => {} + Rule::throw => {} + _ => unreachable!() + } + } + + Function { + index: index.unwrap(), + id: id.unwrap(), + async_, + direction, + arguments, return_: returns, parameters, throws, + } +} +*/ + +/* +fn to_argument(pairs: Pairs) -> Argument { + let mut id: Option = None; + let mut kind: Option = None; + + for pair in pairs { + match pair.as_rule() { + Rule::id => id = Some(pair.as_str().to_owned()), + Rule::kind => kind = Some(primitive::to_type(pair.as_str())), + _ => unreachable!() + } + } + + Argument { id, type_: kind.unwrap() } +} +*/ + +pub fn to_expression_block(pair: Pair) -> ASTUnit { + let inner = pair.into_inner().next().unwrap(); + + // let mut expression = vec![]; + let function_calls: Vec = vec![]; + + match inner.as_rule() { + Rule::function_call => { + // expression.push() + }, + r => panic!("Rule not implemented in 'expression_block': {:?}", r) + } + + ASTUnit::ExpressionBlock { + function_calls + } +} diff --git a/src/schema/ir/compiler/interpreted/frozen/blob.rs b/src/schema/ir/compiler/interpreted/frozen/blob.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/interpreted/frozen/commit.rs b/src/schema/ir/compiler/interpreted/frozen/commit.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/interpreted/frozen/mod.rs b/src/schema/ir/compiler/interpreted/frozen/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/interpreted/frozen/tree.rs b/src/schema/ir/compiler/interpreted/frozen/tree.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/interpreted/frozen/unit.rs b/src/schema/ir/compiler/interpreted/frozen/unit.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/interpreted/frozen_unit.rs b/src/schema/ir/compiler/interpreted/frozen_unit.rs new file mode 100644 index 0000000..6c54382 --- /dev/null +++ b/src/schema/ir/compiler/interpreted/frozen_unit.rs @@ -0,0 +1,83 @@ +// Standard Uses + +// Local Uses +use crate::ir::compiler::interpreted::primitive::KindValue; + +// External Uses + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum FrozenUnit { + Tag(String), + Namespace(String), + Import(String), + Constant { + docstring: Option, + name: String, + kind_value: KindValue + }, + Property { + name: String, + expression: Option + }, + Parameter { + name: String, + default_value: String + }, + ExpressionBlock { + function_calls: Vec + }, + // + Enum { + docstring: Option, + name: String, + variants: Vec + }, + EnumVariant(KindValue), + Settings { + docstring: Option, + name: String, + parameters: Vec, + }, + Struct { + docstring: Option, + parameters: Vec, + name: String, + fields: Vec, + }, + Protocol { + docstring: String, + parameters: Vec, + name: String, + functions: Vec + }, + Function { + docstring: String, + name: String, + synchronous: bool, + direction: Box, + arguments: Vec, + returns: Vec, + throws: Vec + }, + Error { + docstring: Option, + parameters: Vec, + name: String, + message: String, + fields: Vec + }, + Validator { + docstring: Option, + properties: Vec, + name: String, + expression_block: Box + }, + Field { + docstring: Option, + parameters: Vec, + optional: bool, + name: String, + kind_value: KindValue, + } +} \ No newline at end of file diff --git a/src/schema/ir/compiler/interpreted/interpreter.rs b/src/schema/ir/compiler/interpreted/interpreter.rs new file mode 100644 index 0000000..065519f --- /dev/null +++ b/src/schema/ir/compiler/interpreted/interpreter.rs @@ -0,0 +1,168 @@ +// Standard Uses + +// Local Uses +use crate::ir::context::Context; +use crate::ir::ast::unit::ASTUnit; +use crate::ir::ast::unit; +use crate::ir::compiler::interpreted::frozen_unit::FrozenUnit; +use crate::ir::compiler::interpreted::primitive; +use crate::ir::compiler::interpreted::primitive::KindValue; +use crate::ir::compiler::interpreted::report::ReportDetails; + +// External Uses + + + +pub fn interpret_unit(context: Context) -> Result, ReportDetails> { + let mut interpreted: Vec = vec![]; + + for unit in &context.main.1 { + use crate::ir::ast::unit::ASTUnit::*; + match unit { + Namespace(n) => { + let unit_namespace = context.find_whole_unit_by_import(n); + + if unit_namespace.is_some() { + return Err(ReportDetails { + kind: "Namespace".to_string(), + message: format!( + "Namespace {} used in another schema: {} and {}, \ + only one schema per namespace is allowed", + n, context.main.0.filename(), unit_namespace.unwrap().0.filename() + ), + start: 0, end: 0, + }) + } + + interpreted.push(FrozenUnit::Namespace(n.clone())); + }, + Import(_) => { + let import = interpret_node(&context, unit)?; + interpreted.push(import); + } + Constant { .. } => { + let constant = interpret_node(&context, unit)?; + interpreted.push(constant); + } + Enum { .. } => { + let r#enum = interpret_node(&context, unit)?; + interpreted.push(r#enum); + } + /* + Unit::Settings { .. } => {} + Unit::Struct { .. } => {} + Unit::Protocol { .. } => {} + Unit::Error { .. } => {} + Unit::Validator { .. } => {} + */ + //r => panic!("Left to impl: {:?}", r) + _ => {} + } + } + + + Ok(interpreted) +} + +pub fn interpret_node(context: &Context, node: &ASTUnit) -> Result { + use crate::ir::ast::unit::ASTUnit::*; + match node { + Tag(_) => { + + } + Namespace(n) => { + let mut found: Option<&Context> = None; + + for relative_ctx in &context.relative_contexts { + if unit::namespace(&relative_ctx.main.1) == n { + if found.is_some() { + return Err(ReportDetails { + kind: "namespace".to_string(), + message: format!( + "Found namespace {} when its already declared in {}", + &n, &relative_ctx.main.0.filename() + ), + start: 0, end: 0, + }) + } + + found = Some(relative_ctx) + } + } + } + Import(i) => { + let relative_unit = context.find_whole_unit_by_import(&i); + + if relative_unit.is_none() { + let relative_unit = relative_unit.unwrap(); + + return Err(ReportDetails { + kind: "import".to_string(), + message: format!("Could not find namespace of {}", relative_unit.0.filename()), + start: 0, end: 0, + }) + } + + return Ok(FrozenUnit::Import(i.clone())) + }, + Constant { name, kind, default_value, .. } => { + let kind_value = primitive::to_kind_value(kind, default_value); + + return Ok(FrozenUnit::Constant { + docstring: None, + name: name.clone(), kind_value + }) + } + Enum { name, variants, .. } => { + let mut frozen_variants: Vec = vec![]; + + for variant in variants { + pub(crate) fn to_variant(variant: &ASTUnit) -> KindValue { + match variant { + EnumVariant { name, kind } => { + if kind.is_none() { + return KindValue::EnumVariant( + name.clone(),None + ) + } + + return KindValue::EnumVariant( + name.clone(), None + ) + }, + _ => panic!("Should not be here") + } + } + + frozen_variants.push(FrozenUnit::EnumVariant( + to_variant(variant, ) + )); + } + + return Ok(FrozenUnit::Enum { + docstring: None, + name: name.clone(), variants: frozen_variants + }) + } + /* + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Error { .. } => {} + Validator { .. } => {} + Field { .. } => {} + Parameter { .. } => {} + Property { .. } => {} + ExpressionBlock { .. } => {} + */ + _ => {} + } + + panic!() +} + +pub fn into_frozen_unit() -> FrozenUnit { + todo!() +} diff --git a/src/schema/ir/compiler/interpreted/mod.rs b/src/schema/ir/compiler/interpreted/mod.rs new file mode 100644 index 0000000..1ca2606 --- /dev/null +++ b/src/schema/ir/compiler/interpreted/mod.rs @@ -0,0 +1,10 @@ +// Relative Modules +pub mod sanitizer; +pub mod serialization; +pub mod primitive; +pub mod interpreter; +pub mod frozen_unit; +pub mod report; + + +// Standard Uses \ No newline at end of file diff --git a/src/schema/ir/compiler/interpreted/primitive.rs b/src/schema/ir/compiler/interpreted/primitive.rs new file mode 100644 index 0000000..f8c843a --- /dev/null +++ b/src/schema/ir/compiler/interpreted/primitive.rs @@ -0,0 +1,71 @@ +// Standard Uses + +// Local Uses + +// External Uses + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +// #[repr(u8)] +pub enum Primitive { + U8(Option), U16(Option), + U32(Option), U64(Option), + U128(Option), + + S8(Option), S16(Option), + S32(Option), S64(Option), + S128(Option), + + // Float(f32), Double(f64), + + String(Option), + Namespaced(Option) +} + + +#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] +pub enum KindValue { + Primitive(Primitive), + EnumVariant(String, Option>), + Union(Vec), + Namespaced(String) +} + + +pub(crate) fn to_kind_value(kind: &String, value: &Option) -> KindValue { + if value.is_none() { + return to_kind_only(kind) + } + + KindValue::Primitive(to_primitive(kind, value.clone().unwrap()).unwrap()) +} + +fn to_kind_only(kind: &str) -> KindValue { + match kind { + &_ => panic!("Cant") + } +} + +fn to_primitive(kind: &str, value: String) -> Option { + use self::Primitive::*; + let kv = match kind { + "u8" => U8(Some(value.parse().unwrap())), + "u16" => U16(Some(value.parse().unwrap())), + "u32" => U32(Some(value.parse().unwrap())), + "u64" => U64(Some(value.parse().unwrap())), + "u128" => U128(Some(value.parse().unwrap())), + + "s8" => S8(Some(value.parse().unwrap())), + "s16" => S16(Some(value.parse().unwrap())), + "s32" => S32(Some(value.parse().unwrap())), + "s64" => S64(Some(value.parse().unwrap())), + "s128" => S128(Some(value.parse().unwrap())), + + "str" => String(Some(value.to_owned())), + _ => { panic!("Got unknown type '{:#}'", kind) } + }; + + Some(kv) +} + + diff --git a/src/schema/ir/compiler/interpreted/report.rs b/src/schema/ir/compiler/interpreted/report.rs new file mode 100644 index 0000000..9ddc966 --- /dev/null +++ b/src/schema/ir/compiler/interpreted/report.rs @@ -0,0 +1,43 @@ +// Standard Uses + +// Local Uses +use crate::ir::context::Context; + +// External Uses +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; + + +#[derive(Debug)] +pub struct ReportDetails { + pub(crate) kind: String, pub(crate) message: String, + pub(crate) start: usize, pub(crate) end: usize +} + +pub fn report_errors(context: Context, errors: Vec) { + let out = Color::Fixed(81); + + let provenance = context.main.0.provenance_details(); + let mut source = (&provenance.0, Source::from(provenance.1)); + + errors.into_iter().for_each(|e| { + Report::build(ReportKind::Error, &provenance.0, 12) + .with_code(3) + .with_message(e.message) + .with_label( + Label::new((&provenance.0, e.start..e.end)) + .with_message(format!("This is of type {}", e.kind.fg(out))) + .with_color(out), + ) + .finish() + .print(&mut source) + .unwrap(); + }); +} + + +#[allow(unused)] +pub fn report_warnings(context: Context, warnings: Vec) { + todo!() +} + + diff --git a/src/ir/sanitizer.rs b/src/schema/ir/compiler/interpreted/sanitizer.rs similarity index 100% rename from src/ir/sanitizer.rs rename to src/schema/ir/compiler/interpreted/sanitizer.rs diff --git a/src/ir/serialization/messagepack.rs b/src/schema/ir/compiler/interpreted/serialization/messagepack.rs similarity index 60% rename from src/ir/serialization/messagepack.rs rename to src/schema/ir/compiler/interpreted/serialization/messagepack.rs index 080aa0f..3e5f154 100644 --- a/src/ir/serialization/messagepack.rs +++ b/src/schema/ir/compiler/interpreted/serialization/messagepack.rs @@ -1,7 +1,7 @@ // Standard Uses // Local Uses -use crate::ir::unit::Unit; +use crate::ir::ast::unit::ASTUnit; // External Uses #[allow(unused)] @@ -10,11 +10,11 @@ use rmp; // https://docs.rs/rmp/latest/rmp/ #[allow(unused)] -fn from_bytes(raw: Vec) -> Unit { +fn from_bytes(raw: Vec) -> ASTUnit { todo!() } #[allow(unused)] -fn to_bytes(unit: Unit) -> Vec { +fn to_bytes(unit: ASTUnit) -> Vec { todo!() } diff --git a/src/stdlib/mod.rs b/src/schema/ir/compiler/interpreted/serialization/mod.rs similarity index 55% rename from src/stdlib/mod.rs rename to src/schema/ir/compiler/interpreted/serialization/mod.rs index 399f174..8eaa988 100644 --- a/src/stdlib/mod.rs +++ b/src/schema/ir/compiler/interpreted/serialization/mod.rs @@ -1,10 +1,13 @@ // Relative Modules -pub mod lang_items; -pub mod validators; - +mod messagepack; // Standard Uses // Local Uses // External Uses + + +#[allow(unused)] +pub struct Origin {} + diff --git a/src/schema/ir/compiler/interpreter/incremental.rs b/src/schema/ir/compiler/interpreter/incremental.rs new file mode 100644 index 0000000..a8fc4b2 --- /dev/null +++ b/src/schema/ir/compiler/interpreter/incremental.rs @@ -0,0 +1,173 @@ +// Standard Uses + +// Local Uses +use crate::ir::ast::unit; +use crate::ir::ast::unit::ASTUnit; +use crate::ir::compiler::Compile; +use crate::ir::compiler::interpreted::frozen_unit::FrozenUnit; +use crate::ir::compiler::interpreted::primitive; +use crate::ir::compiler::interpreted::primitive::KindValue; +use crate::ir::compiler::interpreted::report::ReportDetails; +use crate::ir::context::Context; + +// External Uses + + +#[allow(unused)] +pub struct IncrementalInterpreter { + context: Context +} + +#[allow(unused)] +impl Compile for IncrementalInterpreter { + type Output = (); + + fn from_ast(ast: Vec) -> Self::Output { + todo!() + } +} + +#[allow(unused)] +impl IncrementalInterpreter { + pub fn interpret_unit(&self) -> Result, ReportDetails> { + let mut interpreted: Vec = vec![]; + + for unit in &self.context.main.1 { + use crate::ir::ast::unit::ASTUnit::*; + match unit { + Namespace(n) => { + // let namespace = n; + interpreted.push(FrozenUnit::Namespace(n.clone())); + }, + Import(_) => { + let import = self.interpret_node( unit)?; + interpreted.push(import); + } + Constant { .. } => { + let constant = self.interpret_node(unit)?; + interpreted.push(constant); + } + Enum { .. } => { + let r#enum = self.interpret_node( unit)?; + interpreted.push(r#enum); + } + /* + Unit::Settings { .. } => {} + Unit::Struct { .. } => {} + Unit::Protocol { .. } => {} + Unit::Error { .. } => {} + Unit::Validator { .. } => {} + */ + //r => panic!("Left to impl: {:?}", r) + _ => {} + } + } + + + Ok(interpreted) + } + + pub fn interpret_node(&self, node: &ASTUnit) -> Result { + use crate::ir::ast::unit::ASTUnit::*; + match node { + Tag(_) => { + + } + Namespace(n) => { + let mut found: Option<&Context> = None; + + for relative_ctx in &self.context.relative_contexts { + if unit::namespace(&relative_ctx.main.1) == n { + if found.is_some() { + return Err(ReportDetails { + kind: "namespace".to_string(), + message: format!( + "Found namespace {} when its already declared in {}", + &n, &relative_ctx.main.0.filename() + ), + start: 0, end: 0, + }) + } + + found = Some(relative_ctx) + } + } + } + Import(i) => { + let relative_unit = self.context.find_whole_unit_by_import(&i); + + if relative_unit.is_none() { + let relative_unit = relative_unit.unwrap(); + + return Err(ReportDetails { + kind: "import".to_string(), + message: format!("Could not find namespace of {}", relative_unit.0.filename()), + start: 0, end: 0, + }) + } + + return Ok(FrozenUnit::Import(i.clone())) + }, + Constant { name, kind, default_value, .. } => { + let kind_value = primitive::to_kind_value(kind, default_value); + + return Ok(FrozenUnit::Constant { + docstring: None, + name: name.clone(), kind_value + }) + } + Enum { name, variants, .. } => { + let mut frozen_variants: Vec = vec![]; + + for variant in variants { + pub(crate) fn to_variant(variant: &ASTUnit) -> KindValue { + match variant { + EnumVariant { name, kind } => { + if kind.is_none() { + return KindValue::EnumVariant( + name.clone(),None + ) + } + + return KindValue::EnumVariant( + name.clone(), None + ) + }, + _ => panic!("Should not be here") + } + } + + frozen_variants.push(FrozenUnit::EnumVariant( + to_variant(variant, ) + )); + } + + return Ok(FrozenUnit::Enum { + docstring: None, + name: name.clone(), variants: frozen_variants + }) + } + /* + EnumVariant { .. } => {} + Settings { .. } => {} + Struct { .. } => {} + Protocol { .. } => {} + Function { .. } => {} + Error { .. } => {} + Validator { .. } => {} + Field { .. } => {} + Parameter { .. } => {} + Property { .. } => {} + ExpressionBlock { .. } => {} + */ + _ => {} + } + + panic!() + } + +} + +pub fn into_frozen_unit() -> FrozenUnit { + todo!() +} diff --git a/src/schema/ir/compiler/interpreter/mod.rs b/src/schema/ir/compiler/interpreter/mod.rs new file mode 100644 index 0000000..b49a974 --- /dev/null +++ b/src/schema/ir/compiler/interpreter/mod.rs @@ -0,0 +1 @@ +pub mod incremental; \ No newline at end of file diff --git a/src/schema/ir/compiler/interpreter/relations.rs b/src/schema/ir/compiler/interpreter/relations.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/schema/ir/compiler/mod.rs b/src/schema/ir/compiler/mod.rs new file mode 100644 index 0000000..fc0011a --- /dev/null +++ b/src/schema/ir/compiler/mod.rs @@ -0,0 +1,26 @@ +// Relative Modules +pub mod interpreter; +pub mod interpreted; + +// Standard Uses + +// Local Uses +use crate::idl::parser; +use crate::ir::ast::unit::ASTUnit; + +// External Uses + + + +pub trait Compile { + type Output; + + fn from_ast(ast: Vec) -> Self::Output; + + fn from_source(source: &str) -> Self::Output { + println!("Compiling source: {}", source); + let ast: Vec = parser::parse_into_unit(source).unwrap(); + println!("{:?}", ast); + Self::from_ast(ast) + } +} diff --git a/src/schema/ir/context.rs b/src/schema/ir/context.rs new file mode 100644 index 0000000..c261413 --- /dev/null +++ b/src/schema/ir/context.rs @@ -0,0 +1,51 @@ +// Standard Uses + +// Local Uses +use crate::ir::ast::unit::WholeUnit; +use crate::stdlib; + +// External Uses + + +pub struct Context { + pub main: WholeUnit, + pub relative_contexts: Vec, + + +} + +#[allow(unused)] +impl Context { + pub fn with_main(main: WholeUnit) -> Self{ + Context { main, relative_contexts: vec![] } + } + + pub(crate) fn add_relative_unit(mut self, whole_unit: WholeUnit) { + self.relative_contexts.push( + Context::with_main(whole_unit) + ) + } + + pub(crate) fn add_relative_context(mut self, context: Context) { + todo!() + } + + pub(crate) fn sanitize_units(self) { + todo!() + } + + pub(crate) fn find_whole_unit_by_import(&self, import: &str) -> Option<&WholeUnit> { + let stdlib_unit = stdlib::find_unit(import); + + if stdlib_unit.is_some() { + return Some(stdlib_unit.unwrap()) + } + + None + } + + pub(crate) fn find_context(&self, import: &str) -> Option<&Context> { + todo!() + } +} + diff --git a/src/schema/ir/mod.rs b/src/schema/ir/mod.rs new file mode 100644 index 0000000..9392140 --- /dev/null +++ b/src/schema/ir/mod.rs @@ -0,0 +1,4 @@ +// Relative Modules +pub mod context; +pub mod ast; +pub mod compiler; diff --git a/src/schema/mod.rs b/src/schema/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/stdlib/lang_items.rs b/src/stdlib/lang_items.rs deleted file mode 100644 index 7720525..0000000 --- a/src/stdlib/lang_items.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Standard Uses - -// Local Uses -use crate::ir::new_unit::Unit; -use crate::idl::parser::from_path; - -// External Uses - - -/* -pub const LANGUAGE_ITEMS: [Vec] = *[ - from_path("src/stdlib/validators/string_bounds.ids").unwrap() -]; -*/ diff --git a/src/stdlib/validators/string_bounds.ids b/src/stdlib/validators/string_bounds.ids deleted file mode 100644 index 7415975..0000000 --- a/src/stdlib/validators/string_bounds.ids +++ /dev/null @@ -1,12 +0,0 @@ -namespace std::validators::StringBounds - -/// Checks if a string length is between a minimum and a maximum -/// @min_chars: Minimum length of the string -/// @max_chars: Maximum length of the string -@min_chars=0 max_chars=1024 -validator StringBounds { - assert_equal( - value.length > params.max_chars or value.length < params.min_chars, - "String {value.name} must be more than {params.min_chars} and less than {params.max_chars}" - ) -} diff --git a/tests/idl/congregation.idc b/tests/idl/congregation.idc new file mode 100644 index 0000000..e69de29 diff --git a/tests/idl/mod.rs b/tests/idl/mod.rs deleted file mode 100644 index d5a9c94..0000000 --- a/tests/idl/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod unit; diff --git a/tests/idl/unit.rs b/tests/idl/unit.rs deleted file mode 100644 index c6966f6..0000000 --- a/tests/idl/unit.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Standard Uses - -// Local Uses - -// External Uses -use comline::{idl, ir}; -use comline::idl::constants::SCHEMA_EXTENSION; -use comline::ir::new_unit::Unit; -use panik_handler; -use pretty_assertions::{assert_eq, assert_ne}; -use comline::ir::compiler; -use comline::ir::primitive::TypeValue; - - -#[test] -fn from_raw_to_unit() { - panik_handler::install(); - - let raw = std::fs::read_to_string( - format!("tests/idl/simple.{}", SCHEMA_EXTENSION) - ).unwrap(); - let unit = idl::parser::parse_into_unit(raw.as_str()).unwrap(); - - assert_eq!( - unit, vec![ - Unit::Namespace("tests.idl.simple".to_string()), - Unit::Import("std::validators::StringBounds".to_string()), - Unit::Constant { - docstring: None, name: "POWER".to_string(), - kind: TypeValue::U8, default_value: Some("1".to_string()), - }, - Unit::Constant { - docstring: None, name: "DEFAULT_NAME".to_string(), - kind: TypeValue::String, default_value: Some("f\"flower power: {POWER}\"".to_string()), - }, - Unit::Settings { - docstring: None, name: "Test".to_string(), - parameters: vec![ - Unit::Parameter { - name: "forbid_indexing".to_string(), - default_value: "True".to_string(), - }, - Unit::Parameter { - name: "forbid_optional_indexing".to_string(), - default_value: "True".to_string(), - } - ], - }, - Unit::Enum { - docstring: None, name: "EncryptionMode".to_string(), - variants: vec![ - Unit::EnumVariant { name: "None".to_string(), kind: None }, - Unit::EnumVariant { name: "Encrypt".to_string(), kind: None } - ] - } - ] - ) -} - - -#[test] -fn compile_unit() { - let unit = idl::parser::from_path( - format!("tests/idl/simple.{}", SCHEMA_EXTENSION).as_str() - ).unwrap(); - - let context = ir::context::Context::with_main(unit); - - let interpreted = compiler::interpret_unit(context); - - println!("{:?}", interpreted); -} diff --git a/tests/units/dev.ids b/tests/idl/units/dev.ids similarity index 100% rename from tests/units/dev.ids rename to tests/idl/units/dev.ids diff --git a/tests/idl/units/mod.rs b/tests/idl/units/mod.rs new file mode 100644 index 0000000..57d165d --- /dev/null +++ b/tests/idl/units/mod.rs @@ -0,0 +1,32 @@ +// Standard Uses + +// Local Uses + +// External Uses +use once_cell::sync::Lazy; +use comline::schema::idl; +use comline::schema::idl::ast::unit::ASTUnit; + + +// Unit with a namespace and a constant +#[allow(unused)] +static UNIT_NAMESPACE_CONST_1: Lazy> = Lazy::new( || { + vec![ + ASTUnit::Namespace("units::test".to_owned()), + ASTUnit::Constant { + docstring: vec![], + name: "VERSION".to_owned(), + kind: "u8".to_owned(), + default_value: Some("1".to_owned()), + } + ] +}); + + +#[test] +fn parse_const() { + let syntax = "const VERSION: u8 = 1"; + let ast = idl::parser::parse_into_unit(syntax).unwrap(); + + pretty_assertions::assert_eq!(*UNIT_NAMESPACE_CONST_1, ast); +} diff --git a/tests/units/protocol/args.ids b/tests/idl/units/protocol/args.ids similarity index 100% rename from tests/units/protocol/args.ids rename to tests/idl/units/protocol/args.ids diff --git a/tests/ir/mod.rs b/tests/ir/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index 7135a1f..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod idl; -mod units; \ No newline at end of file diff --git a/tests/stdlib/mod.rs b/tests/stdlib/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/stdlib/validators.rs b/tests/stdlib/validators.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/units/consts.ids b/tests/units/consts.ids deleted file mode 100644 index fe0fcb3..0000000 --- a/tests/units/consts.ids +++ /dev/null @@ -1,2 +0,0 @@ -const VERSION: u8 = 1 - diff --git a/tests/units/mod.rs b/tests/units/mod.rs deleted file mode 100644 index be4569d..0000000 --- a/tests/units/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Standard Uses -use std::string::ToString; - -// Local Uses - -// External Uses -use once_cell::sync::Lazy; -use comline::ir::unit::{Const, Unit}; -use comline::ir::primitive::TypeValue; - - -// Unit with a namespace and a constant -#[allow(unused)] -static UNIT_NAMESPACE_CONST_1: Lazy = Lazy::new( || { - Unit::Items( - vec![ - Unit::Namespace("units.test".to_string()), - Unit::Consts(vec![ - Const { - id: "".to_string(), - type_: TypeValue::U8, - default_value: vec![], - } - ]) - ] - ) -}); -