More work on a lot of subjects
This commit is contained in:
parent
304e521971
commit
965058def2
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -14,5 +14,3 @@ Cargo.lock
|
||||||
*.pdb
|
*.pdb
|
||||||
|
|
||||||
|
|
||||||
# Docs build
|
|
||||||
/docs/.cache/
|
|
||||||
|
|
|
@ -2,9 +2,16 @@
|
||||||
<module type="CPP_MODULE" version="4">
|
<module type="CPP_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/core/examples" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/core/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/core/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/package-manager/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/package-manager-server/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/package-manager/src/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/runtime/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/runtime/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/core_stdlib/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/core/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
21
.idea/inspectionProfiles/Project_Default.xml
Normal file
21
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myValues">
|
||||||
|
<value>
|
||||||
|
<list size="7">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="h3" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="myCustomValuesEnabled" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -1,6 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/core_stdlib" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/package-manager" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/package-manager-server" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/runtime" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
31
Cargo.toml
31
Cargo.toml
|
@ -1,21 +1,12 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "comline"
|
# resolver = "2"
|
||||||
version = "0.1.0"
|
members = [
|
||||||
edition = "2021"
|
"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
|
[profile.release]
|
||||||
|
opt-level = 2
|
||||||
[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"
|
|
||||||
|
|
21
LICENSE
21
LICENSE
|
@ -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.
|
|
11
README.md
11
README.md
|
@ -1,7 +1,12 @@
|
||||||
# Comline
|
# 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
|
Now the command `comline-dev` is available anywhere
|
||||||
Rust
|
|
||||||
|
|
0
.gitattributes → core/.gitattributes
vendored
0
.gitattributes → core/.gitattributes
vendored
18
core/.gitignore
vendored
Normal file
18
core/.gitignore
vendored
Normal file
|
@ -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/
|
8
core/.idea/.gitignore
vendored
Normal file
8
core/.idea/.gitignore
vendored
Normal file
|
@ -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
|
13
core/.idea/comline-rs.iml
Normal file
13
core/.idea/comline-rs.iml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="CPP_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
core/.idea/modules.xml
Normal file
8
core/.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/comline-rs.iml" filepath="$PROJECT_DIR$/.idea/comline-rs.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
core/.idea/vcs.xml
Normal file
6
core/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
32
core/Cargo.toml
Normal file
32
core/Cargo.toml
Normal file
|
@ -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"
|
20
core/README.md
Normal file
20
core/README.md
Normal file
|
@ -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
|
||||||
|
|
32
core/examples/codegen/simple_gen.rs
Normal file
32
core/examples/codegen/simple_gen.rs
Normal file
|
@ -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<String, TellBackError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ValidatorStringBounds<[u8; 8]>)]
|
||||||
|
struct Credentials {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RegisterStatus {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TellBackError {
|
||||||
|
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
|
@ -40,8 +40,6 @@ protocol Mail {
|
||||||
// Maximum amount of articles that a provider can send
|
// Maximum amount of articles that a provider can send
|
||||||
const SENT_ARTICLES_MAX: u8 = 10
|
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 {
|
protocol Feed {
|
||||||
// Fetches new articles since last fetch
|
// Fetches new articles since last fetch
|
||||||
@flexible async stream
|
@flexible async stream
|
|
@ -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
|
// Identifier for this schema, every identifier should be unique, generated by the schema compiler
|
||||||
// so its not necessary to do this manually
|
// so its not necessary to do this manually
|
||||||
tag 0563125512
|
tag 0563125512
|
||||||
|
@ -61,10 +59,3 @@ protocol World provider=Server {
|
||||||
// the caller will abort waiting for the response or return signal
|
// the caller will abort waiting for the response or return signal
|
||||||
async client server function hello_back_both() -> str: timeout_ms=1000
|
async client server function hello_back_both() -> str: timeout_ms=1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Think what »discoverable« should mean, just like Fuchsia IDL
|
|
||||||
@discoverable
|
|
||||||
protocol OpenWorld {
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
namespace examples.thing.simple
|
namespace examples::thing::simple
|
||||||
|
|
||||||
const AGREEMENT_KEY: string = "agreement_key"
|
const AGREEMENT_KEY: string = "agreement_key"
|
||||||
|
|
||||||
@min_chars=16 max_chars=60
|
|
||||||
validator PasswordCheck {
|
validator PasswordCheck {
|
||||||
|
min_chars: u32 = 16
|
||||||
|
max_chars: u32 = 60
|
||||||
|
|
||||||
|
validate = {
|
||||||
assert_valid(
|
assert_valid(
|
||||||
self.parameters.0,
|
self.parameters.0,
|
||||||
StringBounds(min_chars=params.min_chars max_chars=params.max_chars
|
StringBounds(params.min_chars params.max_chars)
|
||||||
|
// StringBounds(min_chars=self.min_chars max_chars=self.max_chars)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@settings(versioned=False validate_fields=False)
|
@settings(versioned=False validate_fields=False)
|
||||||
struct Credentials {
|
struct Credentials {
|
||||||
|
|
7
core/proposals/enum_variant_associated_kind.ids
Normal file
7
core/proposals/enum_variant_associated_kind.ids
Normal file
|
@ -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)
|
||||||
|
}
|
8
core/proposals/protocol_function_blanket_impl.ids
Normal file
8
core/proposals/protocol_function_blanket_impl.ids
Normal file
|
@ -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
|
||||||
|
}
|
5
core/proposals/protocol_property_discoverable.ids
Normal file
5
core/proposals/protocol_property_discoverable.ids
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// TODO: Think what »discoverable« should mean, just like Fuchsia IDL
|
||||||
|
@discoverable
|
||||||
|
protocol OpenWorld {
|
||||||
|
}
|
||||||
|
|
13
core/proposals/protocol_provider_and_provenance.ids
Normal file
13
core/proposals/protocol_provider_and_provenance.ids
Normal file
|
@ -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]
|
||||||
|
}
|
44
core/src/autodoc/document.rs
Normal file
44
core/src/autodoc/document.rs
Normal file
|
@ -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<String>,
|
||||||
|
minor_changes: Vec<String>
|
||||||
|
}
|
||||||
|
impl Document {
|
||||||
|
pub fn new() -> Box<Self> {
|
||||||
|
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<String>, new: &Vec<String>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn on_dependencies_changed(&mut self, old: &Vec<Dependency>, new: &Vec<Dependency>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
50
core/src/autodoc/mod.rs
Normal file
50
core/src/autodoc/mod.rs
Normal file
|
@ -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<FrozenUnit>, to: &Vec<FrozenUnit>) -> Document {
|
||||||
|
let mut listeners: Vec<Box<dyn Differ>> = vec![Document::new()];
|
||||||
|
|
||||||
|
diff::match_differ(&mut listeners, from, to);
|
||||||
|
|
||||||
|
*listeners.remove(0).downcast::<Document>().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 { .. } => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
6
core/src/autodoc/package.rs
Normal file
6
core/src/autodoc/package.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Crate Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
70
core/src/autodoc/schema.rs
Normal file
70
core/src/autodoc/schema.rs
Normal file
|
@ -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<String>,
|
||||||
|
pub minor_changes: Vec<String>,
|
||||||
|
}
|
||||||
|
#[allow(unused)]
|
||||||
|
impl Document {
|
||||||
|
pub fn new() -> Box<Self> {
|
||||||
|
Box::new(Self { major_changes: vec![], minor_changes: vec![] })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn differ(&self, previous: &Vec<FrozenUnit>, next: &Vec<FrozenUnit>) {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
core/src/codegen/mod.rs
Normal file
28
core/src/codegen/mod.rs
Normal file
|
@ -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<FrozenUnit>) -> String;
|
||||||
|
|
||||||
|
static GENERATORS: Lazy<HashMap<&str, GeneratorFn>> = 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)
|
||||||
|
}
|
102
core/src/codegen/rust.rs
Normal file
102
core/src/codegen/rust.rs
Normal file
|
@ -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<FrozenUnit>) -> 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()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
17
core/src/langlib/lang_items.rs
Normal file
17
core/src/langlib/lang_items.rs
Normal file
|
@ -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<Vec<SourcedWhole>> = Lazy::new(||
|
||||||
|
vec![
|
||||||
|
from_path(Path::new("src/langlib/validators/string_bounds.ids")).unwrap()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
76
core/src/langlib/mod.rs
Normal file
76
core/src/langlib/mod.rs
Normal file
|
@ -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<SpannedUnit>> {
|
||||||
|
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<SpannedUnit>) -> Option<&str> {
|
||||||
|
for (_, variant) in unit {
|
||||||
|
return match variant {
|
||||||
|
ASTUnit::Namespace(_, n) => Some(n),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unit_item<'a>(unit: &'a Vec<SpannedUnit>, 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
|
||||||
|
}
|
|
@ -8,7 +8,10 @@ extern crate serde_derive;
|
||||||
extern crate rmp_serde as rmps;
|
extern crate rmp_serde as rmps;
|
||||||
|
|
||||||
// Relative Modules
|
// Relative Modules
|
||||||
pub mod ir;
|
|
||||||
pub mod codegen;
|
pub mod codegen;
|
||||||
pub mod idl;
|
pub mod schema;
|
||||||
pub mod stdlib;
|
pub mod project;
|
||||||
|
pub mod autodoc;
|
||||||
|
pub mod utils;
|
||||||
|
pub mod report;
|
||||||
|
// pub mod langlib;
|
170
core/src/project/build/mod.rs
Normal file
170
core/src/project/build/mod.rs
Normal file
|
@ -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<String>, args: Args) -> Result<String, RenderError> {
|
||||||
|
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 {
|
||||||
|
|
||||||
|
}
|
49
core/src/project/idl/ast.rs
Normal file
49
core/src/project/idl/ast.rs
Normal file
|
@ -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<DependencyListItem>),
|
||||||
|
List(Vec<ListItem>),
|
||||||
|
Dictionary(Vec<DictKeyValue>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<SpannedUnit>);
|
||||||
|
|
8
core/src/project/idl/constants.rs
Normal file
8
core/src/project/idl/constants.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Local Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
||||||
|
|
||||||
|
pub const CONGREGATION_EXTENSION: &str = "idp";
|
130
core/src/project/idl/idc.pest
Normal file
130
core/src/project/idl/idc.pest
Normal file
|
@ -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)*
|
||||||
|
~ "*/"
|
||||||
|
}
|
5
core/src/project/idl/mod.rs
Normal file
5
core/src/project/idl/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod ast;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod constants;
|
||||||
|
pub mod parser_new;
|
47
core/src/project/idl/parser.rs
Normal file
47
core/src/project/idl/parser.rs
Normal file
|
@ -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<super::ast::WholeUnit> {
|
||||||
|
if !path.exists() { bail!("Path doesn't exist: {:?}", path) }
|
||||||
|
|
||||||
|
from_path_str(path.to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_path_str(path: &str) -> Result<super::ast::WholeUnit> {
|
||||||
|
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<super::ast::VUnit> {
|
||||||
|
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<Rule>) -> Result<super::ast::ASTUnit> {
|
||||||
|
match pair.as_rule() {
|
||||||
|
missing => { panic!("Rule not implemented: {:?}", missing) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
179
core/src/project/idl/parser_new.rs
Normal file
179
core/src/project/idl/parser_new.rs
Normal file
|
@ -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<SourcedWhole> {
|
||||||
|
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<SourcedWhole> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<AssignmentUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<ListItem> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
core/src/project/ir/compiler/interpret.rs
Normal file
27
core/src/project/ir/compiler/interpret.rs
Normal file
|
@ -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<ProjectContext>) -> 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(())
|
||||||
|
}
|
25
core/src/project/ir/compiler/mod.rs
Normal file
25
core/src/project/ir/compiler/mod.rs
Normal file
|
@ -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<ASTUnit>) -> Self::Output;
|
||||||
|
|
||||||
|
fn from_sourced_whole(sourced: SourcedWhole) -> Self::Output;
|
||||||
|
|
||||||
|
fn from_source(source: &str) -> Self::Output;
|
||||||
|
|
||||||
|
fn from_origin(origin: &Path) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
39
core/src/project/ir/compiler/report.rs
Normal file
39
core/src/project/ir/compiler/report.rs
Normal file
|
@ -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<dyn std::error::Error + Send + Sync> }
|
||||||
|
SpecVersionNotTold { source: io::Error },
|
||||||
|
|
||||||
|
// #[strum(props(Id="2"))]
|
||||||
|
#[snafu(display("Schema paths list is empty, at least one schema is required'"))]
|
||||||
|
// SpecVersionNotTold { source: Box<dyn std::error::Error + Send + Sync> }
|
||||||
|
SchemaPathsEmpty,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impl From<CompileError> for Error {
|
||||||
|
fn from(e: CompilerError) -> Box<Self> {
|
||||||
|
super::Error::IO {
|
||||||
|
source: Box::new(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub type CompileResult<T, E = CompileError> = std::result::Result<T, E>;
|
117
core/src/project/ir/context.rs
Normal file
117
core/src/project/ir/context.rs
Normal file
|
@ -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<Rc<RefCell<SchemaContext>>>,
|
||||||
|
|
||||||
|
pub relative_projects: Vec<Rc<ProjectContext>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[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<ProjectContext>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_schema_context(&mut self, context: Rc<RefCell<SchemaContext>>) {
|
||||||
|
self.schema_contexts.push(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sanitize_units(self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_schema_by_import(&self, import: &str) -> Option<&Rc<RefCell<SchemaContext>>> {
|
||||||
|
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<Rc<SchemaContext>> {
|
||||||
|
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<PathBuf> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
117
core/src/project/ir/diff/mod.rs
Normal file
117
core/src/project/ir/diff/mod.rs
Normal file
|
@ -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<String>, new: &Vec<String>);
|
||||||
|
fn on_dependencies_changed(&mut self, old: &Vec<Dependency>, new: &Vec<Dependency>);
|
||||||
|
|
||||||
|
// fn on_assignment_changed(&self, old: AssignmentUnit, new: AssignmentUnit);
|
||||||
|
|
||||||
|
fn differ(
|
||||||
|
&self, previous: &Vec<FrozenUnit>, 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<FrozenUnit>, 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<Box<dyn Differ>> = 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::<Document>().unwrap();
|
||||||
|
let versioning = listeners[1].downcast_ref::<Versioning>().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn match_differ(
|
||||||
|
mut listeners: &mut Vec<Box<dyn Differ>>,
|
||||||
|
previous: &Vec<FrozenUnit>, 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<FrozenUnit>) -> 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
|
||||||
|
}
|
30
core/src/project/ir/diff/report.rs
Normal file
30
core/src/project/ir/diff/report.rs
Normal file
|
@ -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<dyn std::error::Error + Send + Sync> }
|
||||||
|
SpecVersionNotTold { source: io::Error },
|
||||||
|
|
||||||
|
// #[strum(props(Id="2"))]
|
||||||
|
#[snafu(display("Schema paths list is empty, at least one schema is required'"))]
|
||||||
|
// SpecVersionNotTold { source: Box<dyn std::error::Error + Send + Sync> }
|
||||||
|
SchemaPathsEmpty,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// pub type CompileResult<T, E = CompileError> = std::result::Result<T, E>;
|
60
core/src/project/ir/diff/versioning.rs
Normal file
60
core/src/project/ir/diff/versioning.rs
Normal file
|
@ -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<Self> {
|
||||||
|
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<String>, new: &Vec<String>) {
|
||||||
|
self.bump_minor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn on_dependencies_changed(&mut self, old: &Vec<Dependency>, new: &Vec<Dependency>) {
|
||||||
|
self.bump_minor = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change() {
|
||||||
|
// use semver::Version;
|
||||||
|
// version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn version_from(nodes: &Vec<FrozenUnit>) -> Result<&String, ()> {
|
||||||
|
for node in nodes {
|
||||||
|
match node {
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(())
|
||||||
|
}
|
66
core/src/project/ir/frozen/loader.rs
Normal file
66
core/src/project/ir/frozen/loader.rs
Normal file
|
@ -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<FrozenWhole> {
|
||||||
|
// let context = from_frozen_objects(&project_path);
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn from_frozen_objects(path: &PathBuf) -> Result<Vec<FrozenUnit>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_latest_frozen(path: &PathBuf) -> Option<Vec<FrozenUnit>> {
|
||||||
|
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<Vec<Vec<FrozenUnit>>> {
|
||||||
|
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)?;
|
||||||
|
*/
|
67
core/src/project/ir/frozen/meta.rs
Normal file
67
core/src/project/ir/frozen/meta.rs
Normal file
|
@ -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<String, String>,
|
||||||
|
}
|
||||||
|
impl Index {
|
||||||
|
pub fn from_project_meta_index(path: &Path) -> Result<Self> {
|
||||||
|
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<u8>) {
|
||||||
|
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<Vec<FrozenUnit>> {
|
||||||
|
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<FrozenUnit> = rmp_serde::from_slice(&*contents)?;
|
||||||
|
|
||||||
|
Ok(deserialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_processed(units: Vec<FrozenUnit>) -> (String, Vec<u8>) {
|
||||||
|
let mut output = vec![];
|
||||||
|
units.serialize(&mut Serializer::new(&mut output)).unwrap();
|
||||||
|
let hash = blake3::hash(&*output);
|
||||||
|
|
||||||
|
(hash.to_string(), output)
|
||||||
|
}
|
61
core/src/project/ir/frozen/mod.rs
Normal file
61
core/src/project/ir/frozen/mod.rs
Normal file
|
@ -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<RefCell<ProjectContext>>, Vec<FrozenUnit>);
|
||||||
|
|
||||||
|
|
||||||
|
#[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<String>,
|
||||||
|
pub(crate) generation_path: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<FrozenUnit>)
|
||||||
|
-> FilterMap<Iter<FrozenUnit>, fn(&FrozenUnit) -> Option<&str>>
|
||||||
|
{
|
||||||
|
units.iter().filter_map(|unit| {
|
||||||
|
match unit {
|
||||||
|
FrozenUnit::SchemaPath(path) => Some(path),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
212
core/src/project/ir/interpreter/freezing.rs
Normal file
212
core/src/project/ir/interpreter/freezing.rs
Normal file
|
@ -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<Vec<FrozenUnit>, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
// 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<Vec<FrozenUnit>, ReportDetails<CompileError>> {
|
||||||
|
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<DictKeyValue>)
|
||||||
|
-> Result<Vec<FrozenUnit>, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
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<DictKeyValue>)
|
||||||
|
-> Result<Vec<FrozenUnit>, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
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<FrozenUnit>
|
||||||
|
) -> Result<FrozenWhole, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
// Ok((Rc::from(context), interpreted))
|
||||||
|
}
|
||||||
|
|
32
core/src/project/ir/interpreter/interpret.rs
Normal file
32
core/src/project/ir/interpreter/interpret.rs
Normal file
|
@ -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<FrozenWhole, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
let mut interpreted: Vec<FrozenUnit> = 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)
|
||||||
|
}
|
57
core/src/project/ir/interpreter/mod.rs
Normal file
57
core/src/project/ir/interpreter/mod.rs
Normal file
|
@ -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<ASTUnit>) -> 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()
|
||||||
|
}
|
||||||
|
}
|
50
core/src/project/ir/interpreter/report.rs
Normal file
50
core/src/project/ir/interpreter/report.rs
Normal file
|
@ -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<u32>, schema_paths: Option<Vec<String>>,
|
||||||
|
dependencies: Option<Vec<Dependency>>, assignments: Vec<AssignmentUnit>
|
||||||
|
) -> 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<u32>, schema_paths: Option<Vec<String>>,
|
||||||
|
dependencies: Option<Vec<Dependency>>, assignments: Vec<AssignmentUnit>
|
||||||
|
) -> Vec<ReportDetails<CompileWarning>> {
|
||||||
|
let warnings = vec![];
|
||||||
|
|
||||||
|
|
||||||
|
warnings
|
||||||
|
}
|
14
core/src/project/ir/mod.rs
Normal file
14
core/src/project/ir/mod.rs
Normal file
|
@ -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
|
||||||
|
|
||||||
|
|
41
core/src/project/mod.rs
Normal file
41
core/src/project/mod.rs
Normal file
|
@ -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<Dependency> {
|
||||||
|
todo!()
|
||||||
|
|
||||||
|
/*
|
||||||
|
for node in self.configuration.1 {
|
||||||
|
match node {
|
||||||
|
ASTUnit::Congregation(_) => {}
|
||||||
|
ASTUnit::SpecificationVersion(_) => {}
|
||||||
|
ASTUnit::Schemas(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
54
core/src/report.rs
Normal file
54
core/src/report.rs
Normal file
|
@ -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<T> {
|
||||||
|
// pub id: u16,
|
||||||
|
// pub message: String,
|
||||||
|
pub(crate) phantom: PhantomData<T>, // pub kind: T,
|
||||||
|
pub span: Option<Span>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl<T> ReportDetails<T> {
|
||||||
|
fn code(&self) -> u8 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn report_errors(context: SchemaContext, errors: Vec<ReportDetails<CompileError>>) {
|
||||||
|
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<ReportDetails<CompileError>>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
3
core/src/schema/idl/ast/mod.rs
Normal file
3
core/src/schema/idl/ast/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod unit;
|
||||||
|
pub mod old_unit;
|
|
@ -1,11 +1,10 @@
|
||||||
// Standard Uses
|
// Standard Uses
|
||||||
|
|
||||||
// Local Uses
|
// Local Uses
|
||||||
use crate::ir::primitive::{Primitive, TypeValue};
|
|
||||||
|
|
||||||
// External Uses
|
// External Uses
|
||||||
|
|
||||||
|
/*
|
||||||
/// Intermediate Representation Unit
|
/// Intermediate Representation Unit
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[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)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
@ -46,7 +31,7 @@ pub struct Settings {
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Const {
|
pub struct Const {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub type_: TypeValue,
|
pub type_: Kind,
|
||||||
pub default_value: Vec<u8>
|
pub default_value: Vec<u8>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +54,7 @@ pub struct Enum {
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct EnumVariant {
|
pub struct EnumVariant {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub type_: Option<TypeValue>
|
pub type_: Option<Kind>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -93,7 +78,7 @@ pub struct Field {
|
||||||
pub index: u8,
|
pub index: u8,
|
||||||
pub optional: bool,
|
pub optional: bool,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub type_: TypeValue,
|
pub type_: Kind,
|
||||||
pub default_value: Vec<u8>,
|
pub default_value: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +102,7 @@ pub struct Function {
|
||||||
pub async_: bool,
|
pub async_: bool,
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
pub arguments: Vec<Argument>,
|
pub arguments: Vec<Argument>,
|
||||||
pub return_: Vec<TypeValue>,
|
pub return_: Vec<Kind>,
|
||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
pub throws: Vec<String>
|
pub throws: Vec<String>
|
||||||
}
|
}
|
||||||
|
@ -126,11 +111,6 @@ pub struct Function {
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Argument {
|
pub struct Argument {
|
||||||
pub id: Option<String>,
|
pub id: Option<String>,
|
||||||
pub type_: TypeValue
|
pub type_: Kind
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
#[allow(unused)]
|
|
||||||
pub fn from_binary(data: Vec<u8>) -> Unit {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
167
core/src/schema/idl/ast/unit.rs
Normal file
167
core/src/schema/idl/ast/unit.rs
Normal file
|
@ -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<String>,
|
||||||
|
description: String
|
||||||
|
},
|
||||||
|
Constant {
|
||||||
|
docstring: Vec<ASTUnit>,
|
||||||
|
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<String>
|
||||||
|
},
|
||||||
|
//
|
||||||
|
Enum {
|
||||||
|
docstring: Vec<ASTUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
variants: Vec<SpannedUnit>
|
||||||
|
},
|
||||||
|
EnumVariant {
|
||||||
|
name: (Span, String),
|
||||||
|
kind: Option<(Span, String)>
|
||||||
|
},
|
||||||
|
Settings {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
},
|
||||||
|
Struct {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
fields: Vec<SpannedUnit>,
|
||||||
|
},
|
||||||
|
Protocol {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
functions: Vec<SpannedUnit>
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
asynchronous: Option<Span>,
|
||||||
|
// direction: Direction,
|
||||||
|
arguments: Vec<SpannedUnit>,
|
||||||
|
// returns: Vec<ASTUnit>,
|
||||||
|
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<SpannedUnit>,
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
properties: Vec<SpannedUnit>,
|
||||||
|
fields: Vec<SpannedUnit>
|
||||||
|
},
|
||||||
|
Validator {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
properties: Vec<SpannedUnit>,
|
||||||
|
name: (Span, String),
|
||||||
|
expression_block: Box<SpannedUnit>
|
||||||
|
},
|
||||||
|
Field {
|
||||||
|
docstring: Vec<SpannedUnit>,
|
||||||
|
parameters: Vec<SpannedUnit>,
|
||||||
|
// index: OrderIndex,
|
||||||
|
optional: bool,
|
||||||
|
name: String,
|
||||||
|
kind: String,
|
||||||
|
default_value: Option<String>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn namespace(units: &Vec<SpannedUnit>) -> &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<Unit>
|
||||||
|
},
|
||||||
|
Node {
|
||||||
|
index: u32,
|
||||||
|
start_position: u32, length: u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SpannedUnit = (Span, ASTUnit);
|
||||||
|
pub type SourcedWhole = (CodeMap, Vec<SpannedUnit>);
|
||||||
|
pub type SourcedWholeRc = (CodeMap, Vec<Rc<SpannedUnit>>);
|
||||||
|
|
||||||
|
|
||||||
|
pub trait Details<'a> {
|
||||||
|
fn find_namespace(&self) -> Option<&'a SpannedUnit>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Details<'a> for &'a Vec<SpannedUnit> {
|
||||||
|
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<Rc<SpannedUnit>> {
|
||||||
|
fn find_namespace(&self) -> Option<&'a SpannedUnit> {
|
||||||
|
for unit in self.iter() {
|
||||||
|
match &unit.1 {
|
||||||
|
ASTUnit::Namespace(_, _) => return Some(unit),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
9
core/src/schema/idl/constants.rs
Normal file
9
core/src/schema/idl/constants.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Local Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
||||||
|
|
||||||
|
pub const SCHEMA_EXTENSION: &str = "ids";
|
||||||
|
// pub const UNIT_EXTENSION: &str = "idu";
|
261
core/src/schema/idl/idl.pest
Normal file
261
core/src/schema/idl/idl.pest
Normal file
|
@ -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)*
|
||||||
|
~ "*/"
|
||||||
|
}
|
5
core/src/schema/idl/mod.rs
Normal file
5
core/src/schema/idl/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod constants;
|
||||||
|
pub mod ast;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod parser_new;
|
389
core/src/schema/idl/parser.rs
Normal file
389
core/src/schema/idl/parser.rs
Normal file
|
@ -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<Rule>) -> Result<ASTUnit> {
|
||||||
|
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<ASTUnit> = vec![];
|
||||||
|
let mut id: Option<String> = None;
|
||||||
|
let mut kind: Option<String> = None;
|
||||||
|
let mut default_value: Option<String> = 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<ASTUnit> = vec![];
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut parameters: Vec<ASTUnit> = 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<ASTUnit> = vec![];
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut variants: Vec<ASTUnit> = 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<ASTUnit> = vec![];
|
||||||
|
let mut parameters: Vec<ASTUnit> = vec![];
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut fields: Vec<ASTUnit> = 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<ASTUnit> = vec![];
|
||||||
|
let mut properties: Vec<ASTUnit> = vec![];
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut expression_block: Option<ASTUnit> = 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<String> = 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<Rule>, file: &Arc<FileMap>) -> 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<String> = None;
|
||||||
|
let mut description: Option<String> = 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<Rule>) -> Vec<Unit> {
|
||||||
|
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<Rule>) -> Vec<Parameter> {
|
||||||
|
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<Rule>) -> Option<String> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> ASTUnit {
|
||||||
|
let pairs = pair.into_inner();
|
||||||
|
|
||||||
|
let mut docstring = vec![];
|
||||||
|
let mut parameters = vec![];
|
||||||
|
let mut optional: bool = false;
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut kind: Option<String> = None;
|
||||||
|
let mut default_value: Option<String> = 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<Rule>, file: &Arc<FileMap>) -> ASTUnit {
|
||||||
|
let inner = pair.into_inner().next().unwrap();
|
||||||
|
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut expression: Option<String> = 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<Rule>) -> ASTUnit {
|
||||||
|
let inner = pair.into_inner();
|
||||||
|
|
||||||
|
let mut synchronous = true;
|
||||||
|
// let mut direction = Direction::Both;
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let arguments: Vec<ASTUnit> = 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<Rule>) -> Argument {
|
||||||
|
let mut id: Option<String> = None;
|
||||||
|
let mut kind: Option<TypeValue> = 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<Rule>) -> ASTUnit {
|
||||||
|
let inner = pair.into_inner().next().unwrap();
|
||||||
|
|
||||||
|
// let mut expression = vec![];
|
||||||
|
let function_calls: Vec<String> = vec![];
|
||||||
|
|
||||||
|
match inner.as_rule() {
|
||||||
|
Rule::function_call => {
|
||||||
|
// expression.push()
|
||||||
|
},
|
||||||
|
r => panic!("Rule not implemented in 'expression_block': {:?}", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTUnit::ExpressionBlock {
|
||||||
|
function_calls
|
||||||
|
}
|
||||||
|
}
|
478
core/src/schema/idl/parser_new.rs
Normal file
478
core/src/schema/idl/parser_new.rs
Normal file
|
@ -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<SourcedWholeRc> {
|
||||||
|
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<SourcedWholeRc> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<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<String> = None;
|
||||||
|
let mut description: Option<String> = 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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<String> = None;
|
||||||
|
let mut kind: Option<String> = None;
|
||||||
|
let mut default_value: Option<String> = 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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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<Rule>, file: &Arc<FileMap>) -> Result<SpannedUnit> {
|
||||||
|
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
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
301
core/src/schema/ir/compiler/interpreted/interpreter.rs
Normal file
301
core/src/schema/ir/compiler/interpreted/interpreter.rs
Normal file
|
@ -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<RefCell<SchemaContext>>, project_context: &RefMut<ProjectContext>
|
||||||
|
) -> Result<(), ReportDetails<CompileError>> {
|
||||||
|
let mut interpreted: Vec<FrozenUnit> = 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<FrozenUnit, ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
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<FrozenUnit> = 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<SpannedUnit>) -> Vec<FrozenUnit> {
|
||||||
|
let properties = vec![];
|
||||||
|
|
||||||
|
for node in nodes {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_frozen_unit() -> FrozenUnit {
|
||||||
|
todo!()
|
||||||
|
}
|
6
core/src/schema/ir/compiler/interpreted/mod.rs
Normal file
6
core/src/schema/ir/compiler/interpreted/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod sanitizer;
|
||||||
|
pub mod serialization;
|
||||||
|
pub mod primitive;
|
||||||
|
pub mod interpreter;
|
||||||
|
|
186
core/src/schema/ir/compiler/interpreted/primitive.rs
Normal file
186
core/src/schema/ir/compiler/interpreted/primitive.rs
Normal file
|
@ -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<u8>) = 0,
|
||||||
|
|
||||||
|
#[strum(props(Name="u16", Description="unsigned 2 bytes, 16 bits"))]
|
||||||
|
U16(Option<u16>),
|
||||||
|
|
||||||
|
#[strum(props(Name="u32", Description="unsigned 4 bytes, 32 bits"))]
|
||||||
|
U32(Option<u32>),
|
||||||
|
|
||||||
|
#[strum(props(Name="u64", Description="unsigned 8 bytes, 64 bits"))]
|
||||||
|
U64(Option<u64>),
|
||||||
|
|
||||||
|
#[strum(props(Name="u128", Description="unsigned 16 bytes, 128 bits"))]
|
||||||
|
U128(Option<u128>),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
S8(Option<u8>),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
S16(Option<u16>),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
S32(Option<u32>), S64(Option<u64>),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
S128(Option<u128>),
|
||||||
|
|
||||||
|
// Float(f32), Double(f64),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
String(Option<String>),
|
||||||
|
|
||||||
|
#[strum(props(Name="", Description=""))]
|
||||||
|
Namespaced(Option<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Primitive {
|
||||||
|
pub fn value_str(&self) -> Option<String> {
|
||||||
|
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<Primitive>, 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<Box<KindValue>>),
|
||||||
|
Union(Vec<KindValue>),
|
||||||
|
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<RefCell<SchemaContext>>,
|
||||||
|
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<RefCell<SchemaContext>>,
|
||||||
|
kind: &(Span, String), value: String
|
||||||
|
) -> Option<Primitive> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
12
core/src/schema/ir/compiler/interpreted/sanitizer.rs
Normal file
12
core/src/schema/ir/compiler/interpreted/sanitizer.rs
Normal file
|
@ -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!()
|
||||||
|
}
|
|
@ -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<u8>) -> ASTUnit {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn to_bytes(unit: ASTUnit) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
173
core/src/schema/ir/compiler/interpreter/incremental.rs
Normal file
173
core/src/schema/ir/compiler/interpreter/incremental.rs
Normal file
|
@ -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<ASTUnit>) -> Self::Output {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl IncrementalInterpreter {
|
||||||
|
pub fn interpret_unit(&self) -> Result<Vec<FrozenUnit>, ReportDetails> {
|
||||||
|
let mut interpreted: Vec<FrozenUnit> = 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<FrozenUnit, ReportDetails> {
|
||||||
|
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<FrozenUnit> = 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!()
|
||||||
|
}
|
100
core/src/schema/ir/compiler/interpreter/meta_stage.rs
Normal file
100
core/src/schema/ir/compiler/interpreter/meta_stage.rs
Normal file
|
@ -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<RefCell<SchemaContext>>, project_context: &RefMut<ProjectContext>
|
||||||
|
) -> Result<(), ReportDetails<CompileError>> {
|
||||||
|
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(())
|
||||||
|
}
|
4
core/src/schema/ir/compiler/interpreter/mod.rs
Normal file
4
core/src/schema/ir/compiler/interpreter/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod meta_stage;
|
||||||
|
pub mod object_stage;
|
||||||
|
pub mod semi_frozen;
|
17
core/src/schema/ir/compiler/interpreter/object_stage.rs
Normal file
17
core/src/schema/ir/compiler/interpreter/object_stage.rs
Normal file
|
@ -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<RefCell<SchemaContext>>
|
||||||
|
) -> Result<(), ReportDetails<CompileError>>
|
||||||
|
{
|
||||||
|
todo!()
|
||||||
|
}
|
14
core/src/schema/ir/compiler/interpreter/semi_frozen.rs
Normal file
14
core/src/schema/ir/compiler/interpreter/semi_frozen.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Crate Uses
|
||||||
|
use crate::utils::codemap::Span;
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
||||||
|
|
||||||
|
pub enum SemiFrozenUnit {
|
||||||
|
Constant {
|
||||||
|
name: (Span, String)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
33
core/src/schema/ir/compiler/mod.rs
Normal file
33
core/src/schema/ir/compiler/mod.rs
Normal file
|
@ -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<ASTUnit>) -> 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;
|
||||||
|
}
|
47
core/src/schema/ir/compiler/report.rs
Normal file
47
core/src/schema/ir/compiler/report.rs
Normal file
|
@ -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<CompileError> {
|
||||||
|
#[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
|
||||||
|
}
|
73
core/src/schema/ir/context.rs
Normal file
73
core/src/schema/ir/context.rs
Normal file
|
@ -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<String>,
|
||||||
|
pub consts: HashMap<Rc<SpannedUnit>, SemiFrozenUnit>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompileState {
|
||||||
|
pub(crate) fn to_frozen(&self) -> Vec<FrozenUnit> {
|
||||||
|
let mut interpreted = vec![];
|
||||||
|
|
||||||
|
interpreted.push(FrozenUnit::Namespace(self.namespace.clone().unwrap()));
|
||||||
|
|
||||||
|
return interpreted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SchemaContext {
|
||||||
|
pub schema: SourcedWholeRc,
|
||||||
|
pub frozen_schema: Option<Vec<FrozenUnit>>,
|
||||||
|
pub project_context: Option<Rc<RefCell<ProjectContext>>>,
|
||||||
|
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<RefCell<ProjectContext>>
|
||||||
|
) -> 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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
118
core/src/schema/ir/diff/mod.rs
Normal file
118
core/src/schema/ir/diff/mod.rs
Normal file
|
@ -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<FrozenUnit>, next: &FrozenWhole,
|
||||||
|
document_gen: bool, auto_version: bool
|
||||||
|
) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl_downcast!(Differ);
|
||||||
|
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn match_differ(
|
||||||
|
mut listeners: &mut Vec<Box<dyn Differ>>,
|
||||||
|
previous: &Vec<FrozenUnit>, next: &Vec<FrozenUnit>
|
||||||
|
) {
|
||||||
|
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<FrozenUnit>)
|
||||||
|
-> 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
|
||||||
|
}
|
62
core/src/schema/ir/frozen/blob.rs
Normal file
62
core/src/schema/ir/frozen/blob.rs
Normal file
|
@ -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<FrozenBlob>) -> (String, Vec<u8>) {
|
||||||
|
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<u8>) -> Result<Vec<FrozenBlob>> {
|
||||||
|
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())])
|
||||||
|
}
|
18
core/src/schema/ir/frozen/commit.rs
Normal file
18
core/src/schema/ir/frozen/commit.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Crate Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
||||||
|
|
||||||
|
pub enum FrozenCommit {
|
||||||
|
Header(FrozenHeader),
|
||||||
|
Tree(String),
|
||||||
|
Blob(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FrozenHeader {
|
||||||
|
AuthorId(Option<String>),
|
||||||
|
Version(semver::Version),
|
||||||
|
}
|
||||||
|
|
5
core/src/schema/ir/frozen/mod.rs
Normal file
5
core/src/schema/ir/frozen/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod unit;
|
||||||
|
pub mod commit;
|
||||||
|
pub mod tree;
|
||||||
|
pub mod blob;
|
20
core/src/schema/ir/frozen/tree.rs
Normal file
20
core/src/schema/ir/frozen/tree.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Crate Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
|
||||||
|
|
||||||
|
pub enum FrozenTree {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn to_processed(nodes: Vec<FrozenTree>) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn from_processed() -> Vec<FrozenTree> {
|
||||||
|
todo!()
|
||||||
|
}
|
87
core/src/schema/ir/frozen/unit.rs
Normal file
87
core/src/schema/ir/frozen/unit.rs
Normal file
|
@ -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<FrozenUnit>);
|
||||||
|
|
||||||
|
#[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<String>,
|
||||||
|
name: String,
|
||||||
|
kind_value: KindValue
|
||||||
|
},
|
||||||
|
Property {
|
||||||
|
name: String,
|
||||||
|
expression: Option<String>
|
||||||
|
},
|
||||||
|
Parameter {
|
||||||
|
name: String,
|
||||||
|
default_value: String
|
||||||
|
},
|
||||||
|
ExpressionBlock {
|
||||||
|
function_calls: Vec<String>
|
||||||
|
},
|
||||||
|
//
|
||||||
|
Enum {
|
||||||
|
docstring: Option<String>,
|
||||||
|
name: String,
|
||||||
|
variants: Vec<FrozenUnit>
|
||||||
|
},
|
||||||
|
EnumVariant(KindValue),
|
||||||
|
Settings {
|
||||||
|
docstring: Option<String>,
|
||||||
|
name: String,
|
||||||
|
parameters: Vec<FrozenUnit>,
|
||||||
|
},
|
||||||
|
Struct {
|
||||||
|
docstring: Option<String>,
|
||||||
|
parameters: Vec<FrozenUnit>,
|
||||||
|
name: String,
|
||||||
|
fields: Vec<FrozenUnit>,
|
||||||
|
},
|
||||||
|
Protocol {
|
||||||
|
docstring: String,
|
||||||
|
parameters: Vec<FrozenUnit>,
|
||||||
|
name: String,
|
||||||
|
functions: Vec<FrozenUnit>
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
docstring: String,
|
||||||
|
name: String,
|
||||||
|
synchronous: bool,
|
||||||
|
direction: Box<FrozenUnit>,
|
||||||
|
arguments: Vec<FrozenUnit>,
|
||||||
|
returns: Vec<FrozenUnit>,
|
||||||
|
throws: Vec<FrozenUnit>
|
||||||
|
},
|
||||||
|
Error {
|
||||||
|
docstring: Option<String>,
|
||||||
|
parameters: Vec<FrozenUnit>,
|
||||||
|
name: String,
|
||||||
|
message: String,
|
||||||
|
fields: Vec<FrozenUnit>
|
||||||
|
},
|
||||||
|
Validator {
|
||||||
|
docstring: Option<String>,
|
||||||
|
properties: Vec<FrozenUnit>,
|
||||||
|
name: String,
|
||||||
|
expression_block: Box<FrozenUnit>
|
||||||
|
},
|
||||||
|
Field {
|
||||||
|
docstring: Option<String>,
|
||||||
|
parameters: Vec<FrozenUnit>,
|
||||||
|
optional: bool,
|
||||||
|
name: String,
|
||||||
|
kind_value: KindValue,
|
||||||
|
}
|
||||||
|
}
|
5
core/src/schema/ir/mod.rs
Normal file
5
core/src/schema/ir/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod context;
|
||||||
|
pub mod compiler;
|
||||||
|
pub mod frozen;
|
||||||
|
pub mod diff;
|
3
core/src/schema/mod.rs
Normal file
3
core/src/schema/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Relative Modules
|
||||||
|
pub mod idl;
|
||||||
|
pub mod ir;
|
309
core/src/utils/codemap.rs
Normal file
309
core/src/utils/codemap.rs
Normal file
|
@ -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<AtomicUsize>,
|
||||||
|
files: Vec<Arc<FileMap>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<C, F>(&mut self, filename: F, contents: C) -> Arc<FileMap>
|
||||||
|
where F: Into<String>,
|
||||||
|
C: Into<String>,
|
||||||
|
{
|
||||||
|
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<FileMap>] {
|
||||||
|
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<AtomicUsize>,
|
||||||
|
items: RefCell<HashMap<Span, Range<usize>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Range<usize>> {
|
||||||
|
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<usize>) -> Option<Span> {
|
||||||
|
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<HashMap<Span, Range<usize>>> {
|
||||||
|
&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<Token> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
2
core/src/utils/mod.rs
Normal file
2
core/src/utils/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod codemap;
|
||||||
|
pub mod templating;
|
32
core/src/utils/templating.rs
Normal file
32
core/src/utils/templating.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Standard Uses
|
||||||
|
|
||||||
|
// Crate Uses
|
||||||
|
|
||||||
|
// External Uses
|
||||||
|
use eyre::Result;
|
||||||
|
use handlebars::{Handlebars, RenderError};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|
||||||
|
pub trait ResolvePaths<T> {
|
||||||
|
fn resolve_paths(&self, root: &T) -> Result<Self> where Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn recurse_render<T>(path: &str, entity: &T)
|
||||||
|
-> std::result::Result<String, RenderError>
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
46
core/tests/autodoc/mod.rs
Normal file
46
core/tests/autodoc/mod.rs
Normal file
|
@ -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<Vec<FrozenUnit>> = 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<Vec<FrozenUnit>> = 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'"#
|
||||||
|
]);
|
||||||
|
}
|
44
core/tests/idl/ir/mod.rs
Normal file
44
core/tests/idl/ir/mod.rs
Normal file
|
@ -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())]);
|
||||||
|
}
|
||||||
|
|
4
core/tests/idl/mod.rs
Normal file
4
core/tests/idl/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// Relative Imports
|
||||||
|
mod unit;
|
||||||
|
mod ir;
|
||||||
|
// mod stdlib;
|
|
@ -1,7 +1,7 @@
|
||||||
// Simple Schema
|
// 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 POWER: u8 = 1
|
||||||
const DEFAULT_NAME: str = f"flower power: {POWER}"
|
const DEFAULT_NAME: str = f"flower power: {POWER}"
|
||||||
|
@ -11,6 +11,11 @@ settings Test {
|
||||||
forbid_optional_indexing=True
|
forbid_optional_indexing=True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum EncryptionAlgorithm {
|
||||||
|
Bad
|
||||||
|
Medium
|
||||||
|
}
|
||||||
|
|
||||||
enum EncryptionMode {
|
enum EncryptionMode {
|
||||||
None
|
None
|
||||||
Encrypt
|
Encrypt
|
||||||
|
@ -18,11 +23,11 @@ enum EncryptionMode {
|
||||||
|
|
||||||
/// A message that can be sent through the mail protocol
|
/// A message that can be sent through the mail protocol
|
||||||
struct Message {
|
struct Message {
|
||||||
1# name: str = DEFAULT_NAME
|
name: str = DEFAULT_NAME
|
||||||
2# encryption_mode: EncryptionMode = default
|
encryption_mode: EncryptionMode = default
|
||||||
|
|
||||||
@validators=[StringBounds(min_chars=3 max_chars=12)]
|
@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
|
/// Throw when sending a message to a missing recipient
|
||||||
|
@ -30,7 +35,7 @@ error RecipientNotFoundError {
|
||||||
message = "Recipient with name {self.recipient} not found"
|
message = "Recipient with name {self.recipient} not found"
|
||||||
|
|
||||||
@validators=[StringBounds(min_chars=8 max_chars=16)]
|
@validators=[StringBounds(min_chars=8 max_chars=16)]
|
||||||
1# recipient: str
|
recipient: str
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mail API for receiving and sending emails
|
/// Mail API for receiving and sending emails
|
||||||
|
@ -38,6 +43,6 @@ error RecipientNotFoundError {
|
||||||
protocol Mail {
|
protocol Mail {
|
||||||
|
|
||||||
@timeout_ms=1000
|
@timeout_ms=1000
|
||||||
1# function send_message(message: Message) -> str
|
function send_message(message: Message) -> str
|
||||||
! RecipientNotFoundError(function.message.recipient)
|
! RecipientNotFoundError(function.message.recipient)
|
||||||
}
|
}
|
2
core/tests/idl/stdlib/mod.rs
Normal file
2
core/tests/idl/stdlib/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Relative Modules
|
||||||
|
mod validators;
|
101
core/tests/idl/stdlib/validators.rs
Normal file
101
core/tests/idl/stdlib/validators.rs
Normal file
|
@ -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![] })
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
249
core/tests/idl/unit.rs
Normal file
249
core/tests/idl/unit.rs
Normal file
|
@ -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
|
||||||
|
)))
|
||||||
|
*/
|
||||||
|
))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue