More work on a lot of subjects

This commit is contained in:
DOOM1EM8Cover 2023-08-28 20:09:31 +01:00
parent 304e521971
commit 965058def2
227 changed files with 8274 additions and 1200 deletions

2
.gitignore vendored
View file

@ -14,5 +14,3 @@ Cargo.lock
*.pdb *.pdb
# Docs build
/docs/.cache/

View file

@ -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" />

View 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>

View file

@ -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>

View file

@ -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
View file

@ -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.

View file

@ -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

18
core/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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!()
}

View file

@ -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

View file

@ -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 {
}

View file

@ -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 {
assert_valid( min_chars: u32 = 16
self.parameters.0, max_chars: u32 = 60
StringBounds(min_chars=params.min_chars max_chars=params.max_chars
) validate = {
assert_valid(
self.parameters.0,
StringBounds(params.min_chars params.max_chars)
// StringBounds(min_chars=self.min_chars max_chars=self.max_chars)
)
}
} }
@settings(versioned=False validate_fields=False) @settings(versioned=False validate_fields=False)
struct Credentials { struct Credentials {

View 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)
}

View 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
}

View file

@ -0,0 +1,5 @@
// TODO: Think what »discoverable« should mean, just like Fuchsia IDL
@discoverable
protocol OpenWorld {
}

View 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]
}

View 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
View 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 { .. } => {}
}
}
}
*/

View file

@ -0,0 +1,6 @@
// Standard Uses
// Crate Uses
// External Uses

View 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
View 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
View 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()
}
*/

View 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
View 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
}

View file

@ -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;

View 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 {
}

View 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>);

View file

@ -0,0 +1,8 @@
// Standard Uses
// Local Uses
// External Uses
pub const CONGREGATION_EXTENSION: &str = "idp";

View 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)*
~ "*/"
}

View file

@ -0,0 +1,5 @@
// Relative Modules
pub mod ast;
pub mod parser;
pub mod constants;
pub mod parser_new;

View 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) }
}
}
*/

View 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)
}
}

View 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(())
}

View 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;
}

View 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>;

View 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
}
}

View 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
}

View 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>;

View 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(())
}

View 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)?;
*/

View 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)
}

View 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
}
})
}

View 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))
}

View 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)
}

View 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()
}
}

View 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
}

View 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
View 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
View 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!()
}

View file

@ -0,0 +1,3 @@
// Relative Modules
pub mod unit;
pub mod old_unit;

View file

@ -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!()
}

View 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
}
}

View file

@ -0,0 +1,9 @@
// Standard Uses
// Local Uses
// External Uses
pub const SCHEMA_EXTENSION: &str = "ids";
// pub const UNIT_EXTENSION: &str = "idu";

View 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)*
~ "*/"
}

View file

@ -0,0 +1,5 @@
// Relative Modules
pub mod constants;
pub mod ast;
pub mod parser;
pub mod parser_new;

View 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
}
}

View 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
}))
}

View 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!()
}

View file

@ -0,0 +1,6 @@
// Relative Modules
pub mod sanitizer;
pub mod serialization;
pub mod primitive;
pub mod interpreter;

View 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)
}

View 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!()
}

View file

@ -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!()
}

View 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!()
}

View 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(())
}

View file

@ -0,0 +1,4 @@
// Relative Modules
pub mod meta_stage;
pub mod object_stage;
pub mod semi_frozen;

View 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!()
}

View file

@ -0,0 +1,14 @@
// Standard Uses
// Crate Uses
use crate::utils::codemap::Span;
// External Uses
pub enum SemiFrozenUnit {
Constant {
name: (Span, String)
},
}

View 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;
}

View 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
}

View 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!()
}
}

View 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
}

View 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())])
}

View 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),
}

View file

@ -0,0 +1,5 @@
// Relative Modules
pub mod unit;
pub mod commit;
pub mod tree;
pub mod blob;

View 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!()
}

View 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,
}
}

View 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
View file

@ -0,0 +1,3 @@
// Relative Modules
pub mod idl;
pub mod ir;

309
core/src/utils/codemap.rs Normal file
View 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
View file

@ -0,0 +1,2 @@
pub mod codemap;
pub mod templating;

View 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
View 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
View 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
View file

@ -0,0 +1,4 @@
// Relative Imports
mod unit;
mod ir;
// mod stdlib;

View file

@ -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,18 +11,23 @@ settings Test {
forbid_optional_indexing=True forbid_optional_indexing=True
} }
enum EncryptionAlgorithm {
Bad
Medium
}
enum EncryptionMode { enum EncryptionMode {
None None
Encrypt Encrypt
} }
/// 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)
} }

View file

@ -0,0 +1,2 @@
// Relative Modules
mod validators;

View 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
View 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