diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/comline-rs.iml b/.idea/comline-rs.iml new file mode 100644 index 0000000..a6b108e --- /dev/null +++ b/.idea/comline-rs.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3b5cb6d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 24764b8..b9f0d95 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ -# comline-rs - +# Comline +Comline(Communication on Line) is a library + + + +## Language details +Rust diff --git a/docs/docs/planning/generation.md b/docs/docs/planning/coge-generaiton.md similarity index 100% rename from docs/docs/planning/generation.md rename to docs/docs/planning/coge-generaiton.md diff --git a/docs/docs/planning/ir-generation.md b/docs/docs/planning/ir-generation.md new file mode 100644 index 0000000..e0405d7 --- /dev/null +++ b/docs/docs/planning/ir-generation.md @@ -0,0 +1,44 @@ +# Intermediate Representation Generation + +When compiling a schema, the schemas imported within it and details +like metadata, state and so on are written into a lockfile `state.lock` + +This lockfile will contain freezed information about evertyhing to be worried, +the goal being to preserve relations, state, versioning of the schema(and related +schemas) past and present + +Upon locking, any changes made to any of the schemas and their relations, the state +and the differential changes are tracked, and at the same time automatic version and +state changes will be recorded + + +## Version State +Versioning follows SemVer (Semantic Versioning) + +When adding new fields to a any of the [Components](#Components), the minor version +will be increased by 1 in the minor segment + +When making any change to an existing field in any of the [Components](#Components), the +major version will be increaded by 1 in the major segment + + +**IMPORTANT TODO**: This makes `schema.ids` not need indexing manually anywhere, +so the convention `#1 optional ...` where `#1` is indexing, can be removed entirely +(if the system above is defined to substitute this feature, thread carefully) + + +**NOTE TODO**: The idea of a lockfile is to create state freezing, it can be +a specific IR unit format of itself, or maybe a Json one? decide this + + +### Components +A component is any part of the schema that does declarations, such being: + - namespace + - constant + - settings + - structure + - enum + - error + - validator + - protocol + diff --git a/examples/dev.cls b/examples/dev.ids similarity index 95% rename from examples/dev.cls rename to examples/dev.ids index 0b2bf4b..2f8b419 100644 --- a/examples/dev.cls +++ b/examples/dev.ids @@ -9,10 +9,13 @@ settings { allow-debugging=true } -settings MailSettings(struct, protocol) { + +@affects=struct protocol +settings MailSettings { indexed=true } + @settings=MailSettings struct Mail { 1# sender: str diff --git a/examples/example.cls b/examples/example.ids similarity index 100% rename from examples/example.cls rename to examples/example.ids diff --git a/examples/greet.cls b/examples/greet.ids similarity index 100% rename from examples/greet.cls rename to examples/greet.ids diff --git a/examples/simple.cls b/examples/simple.ids similarity index 60% rename from examples/simple.cls rename to examples/simple.ids index 9bc1ca2..9851273 100644 --- a/examples/simple.cls +++ b/examples/simple.ids @@ -3,45 +3,59 @@ namespace examples.thing.simple const AGREEMENT_KEY: string = "agreement_key" -validator StringBounds min_chars=0 max_chars=1024 { +@min_chars=0 max_chars=1024 +validator StringBounds { assert_error( value.length > params.max_chars or value.length < params.min_chars, "String {value.name} must be more than {params.min_chars} and less than {params.max_chars}" ) } -validator PasswordCheck min_chars=16 max_chars=60 { - assert_validate(StringBounds(min_chars=params.min_chars max_chars=params.max_chars) +@min_chars=16 max_chars=60 +validator PasswordCheck { + assert_valid( + self.parameters.0, + StringBounds(min_chars=params.min_chars max_chars=params.max_chars + ) } + @settings(versioned=False validate_fields=False) struct Credentials { + @validators([StringBounds(min_chars=8 max_chars=16)]) 1# username: str = "test" - @validators=[PasswordCheck()] + @validators=[PasswordCheck] 2# password: str } -struct CredentialsOther { - #1 username: union ( - str [validators=[StringBounds(min_chars=8 max_chars=16]], - null - ) = "test" | null - #2 password: str [validators=[PasswordCheck()]] +struct CredentialsOther { + + @parameters.0.validators=[StringBounds(min_chars=8 max_chars=16] + #1 username: union(str null) = "test" | null + + @validators=([PasswordCheck()]) + #2 password: str } + enum RegisterStatus { Ok UsernameTaken } -error TellBackError back="Foo" { - message = "Could not get the provider to tell \"{back}\" back" + +error TellBackError { + message = "Could not get the provider to tell \"{self.back}\" back" + + 1# back: str } -protocol Registry provider=Server { + +@provider=Server +protocol Registry { @argument.0.username.validate=true #timeout_ms=100 @@ -56,12 +70,14 @@ protocol Registry provider=Server { } + struct TellBackResponse { } -@ -protocol Events provider=Server { + +@provider=Server +protocol Events { async function poke() } diff --git a/examples/simple_gen.rs b/examples/simple_gen.rs index 112a270..73b1ff5 100644 --- a/examples/simple_gen.rs +++ b/examples/simple_gen.rs @@ -1,5 +1,5 @@ - +/* trait Registry { fn register(message: Credentials) -> RegisterStatus; fn my_username() -> String; @@ -18,4 +18,7 @@ struct RegisterStatus { struct TellBackError { } +*/ + +fn main() { todo!() } diff --git a/src/idl.rs b/src/idl.rs index 75f6a5d..e91bf23 100644 --- a/src/idl.rs +++ b/src/idl.rs @@ -1 +1,3 @@ -pub mod pest; \ No newline at end of file +pub mod pest; +pub mod parser; +pub mod constants; \ No newline at end of file diff --git a/src/idl/constants.rs b/src/idl/constants.rs new file mode 100644 index 0000000..887e5d7 --- /dev/null +++ b/src/idl/constants.rs @@ -0,0 +1,8 @@ +// Standard Uses + +// Local Uses + +// External Uses + +pub const SCHEMA_EXTENSION: &str = "ids"; +pub const UNIT_EXTENSION: &str = "idu"; diff --git a/src/idl/idl.pest b/src/idl/idl.pest index 7e28758..c53f2cb 100644 --- a/src/idl/idl.pest +++ b/src/idl/idl.pest @@ -1,26 +1,49 @@ // IDL grammar -schema = _{ namespace ~ (COMMENT | settings | constant | validator | enumeration | structure | error | protocol)* } - -namespace = { "namespace" ~ WS ~ domain ~ WS } - -constant = { "const" ~ WS ~ id ~ WS? ~ ":" ~ WS ~ kind ~ WS ~ ("=" ~ WS ~ value)? ~ WS? } - -settings = { - "settings" ~ WS? ~ id? ~ WS? ~ - "{" ~ WS? ~ parameter* ~ WS? ~"}" - ~ WS? +schema = _{ + namespace + ~ ( + COMMENT | import + | settings | constant + | validator | enumeration + | structure | error | protocol + )* } -enumeration = { - "enum" ~ WS ~ id ~ WS? ~ parameter* ~ WS? ~ - "{" ~ WS? ~ enum_variant+ ~ WS? ~ "}" +namespace = { + "namespace" ~ WS ~ domain +} + +import = { + WS ~ "import" ~ WS + ~ domain_namespaced +} + +constant = { + WS ~ "const" ~ WS ~ id + ~ WS? ~ ":" ~ WS? + ~ kind ~ (WS? ~ "=" ~ WS? ~ value)? +} + +settings = { + WS ~ parameter* + ~ "settings" ~ WS? ~ id? ~ WS? + ~ "{" ~ WS? ~ parameter* ~ WS? ~ "}" +} + +enumeration = { + WS ~ parameter* + ~ "enum" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ enum_variant+ ~ WS? ~ "}" ~ WS? } -enum_variant = { digit+ ~ WS? ~ "#" ~ WS ~ id ~ WS? } +enum_variant = { + (index ~ "#")? ~ WS? ~ id ~ WS? +} validator = { - "validator" ~ WS ~ id ~ WS? ~ parameter* ~ WS? ~ - "{" ~ WS? ~ function_call* ~ WS? ~ "}" + WS ~ parameter* + ~ "validator" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ function_call* ~ WS? ~ "}" ~ WS? } @@ -31,22 +54,11 @@ expression = { (operation ~ WS? ~ boolean_operator? ~ WS?)+ } function_call = { - id ~ WS? ~ "(" ~ WS? ~ - (","? ~ WS? ~ function_call_arg)* + id ~ WS? ~ "(" ~ WS? + ~ (","? ~ WS? ~ function_call_arg)* ~ WS? ~ ")" ~ WS? } -function_call_arg = { - expression | string_interpolated -} - -string_interpolated = { - "\"" ~ - (string | WS | - ("{" ~ domain ~ "}"))* - ~ "\"" -} -string = @{ (alpha | digit | WS+)+ } - +function_call_arg = { expression | string } entity = { digit+ | domain } operation = { @@ -55,69 +67,131 @@ operation = { } operator = { - "==" | "!=" | "<" | ">" | "+" | "-" | "/" | "|" + "==" | "!=" + | "<" | ">" + | "+" | "-" | "/" + | "|" } boolean_operator = { "or" | "and" } structure = { - "struct" ~ WS ~ id ~ WS? ~ parameter* ~ WS? ~ - "{" ~ WS? ~ (constant | field)+ ~ WS? ~ "}" + WS? ~ parameter* + ~ "struct" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ (constant | field)+ ~ WS? ~ "}" ~ WS? } field = { - (index ~ "#")? ~ (WS? ~ requirement)? ~ - WS? ~ id ~ WS? ~ ":" ~ WS? ~ kind ~ - (WS? ~ "=" ~ WS? ~ value)? ~ WS? + WS? ~ property* + ~ (index ~ "#")? ~ (WS? ~ requirement)? + ~ WS? ~ id ~ WS? ~ ":" ~ WS? ~ kind + ~ (WS? ~ "=" ~ WS? ~ value)? ~ WS? } -index = { digit } +index = @{ digit } requirement = { "optional" } error = { - "error" ~ WS ~ id ~ WS? ~ parameter* ~ WS? ~ - "{" ~ WS? ~ (parameter | field)+ ~ WS? ~ "}" ~ WS? + WS? ~ property* + ~ "error" ~ WS ~ id ~ WS? + ~ "{" ~ WS? + ~ (parameter | field)+ + ~ WS? ~ "}" ~ WS? } protocol = { - "protocol" ~ WS ~ id ~ WS? ~ parameter* ~ WS? ~ - "{" ~ WS? ~ function+ ~ "}" ~ WS? + WS? ~ property* ~ WS? + ~ "protocol" ~ WS ~ id ~ WS? + ~ "{" ~ WS? ~ function* ~ WS? ~ "}" } function = { - (index ~ WS? ~ "#" ~ WS?)? ~ - (asynchronous ~ WS?)? ~ - (direction ~ WS?)? ~ - "function" ~ WS ~ id ~ WS? ~ - "(" ~ WS? ~ argument* ~ WS? ~ ")" ~ WS? ~ - ("->" ~ WS? ~ returns+ ~ WS?)? ~ - (":" ~ WS? ~ parameter+ ~ WS?)? ~ - ("!" ~ WS? ~ throw+ ~ WS?)? ~ - WS? + WS? ~ property* ~ WS? + ~ (index ~ WS? ~ "#" ~ WS?)? + ~ (asynchronous ~ WS?)? + ~ (direction ~ WS?)? + ~ "function" ~ WS ~ id ~ WS? + ~ "(" ~ WS? ~ argument* ~ WS? ~ ")" + ~ (WS? ~ "->" ~ WS? ~ returns+)? + ~ (WS? ~ ":" ~ WS? ~ parameter+)? + ~ (WS? ~ "!" ~ WS? ~ throw+)? } direction = { "client" | "server" } asynchronous = { "async" } argument = { - ","? ~ WS? ~ - ((id ~ WS? ~ ":" ~ WS? ~ kind) | kind) + ","? ~ WS? + ~ ((id ~ WS? ~ ":" ~ WS? ~ kind) | kind) ~ WS? } returns = { ","? ~ WS? ~ (kind) ~ WS? } parameter = { - ","? ~ WS? ~ id ~ WS? ~ "=" ~ WS? ~ value ~ WS? + WS? ~ id ~ WS? ~ "=" ~ WS? ~ value ~ WS? } throw = { - ","? ~ WS? ~ id ~ WS? + ","? ~ WS? ~ value ~ WS? } + +// Common Rules +property = { + WS? ~ "@" ~ WS? ~ domain ~ WS? ~ "=" + ~ ( + domain_namespaced | domain + | number | property_array + ) + ~ (NEWLINE | WS)* +} +property_array = { + "[" ~ property_instance* ~ "]" +} +property_instance = { + (NEWLINE | WS)* + ~ domain + ~ "(" ~ property_attribute* ~ ")" + ~ (NEWLINE | WS)* +} +property_attribute = { + (NEWLINE | WS)* + ~ id ~ "=" ~ kind +} + + domain = @{ (id | ".")+ } +domain_namespaced = @{ (alpha | "::" | "_")+ } +number = @{ digit+ } id = @{ (alpha | "_")+ } kind = @{ (alpha | digit)+ } -// Replace text with match anything inside " " -value = { ("\"" ~ inner_string ~ "\"") | inner_namespaced } -inner_string = @{ (alpha | digit | WS | "{" | "}" | ".")+ } -inner_namespaced = @{ (alpha | digit | "::")+ } +instantiation = { + (domain | domain_namespaced) + ~ "(" ~ domain ~ ")" +} + +value = { + "true" | "false" + | string | string_interpolated + | instantiation + | 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' } diff --git a/src/idl/parser.rs b/src/idl/parser.rs new file mode 100644 index 0000000..97fa730 --- /dev/null +++ b/src/idl/parser.rs @@ -0,0 +1,36 @@ +// Standard Uses + +// Local Uses +use crate::ir::new_unit::Unit; + +// External Uses +use anyhow::Result; +use pest::iterators::Pair; + + +#[derive(Parser)] +#[grammar = "idl/idl.pest"] +pub struct IDLParser; + + +/* +pub fn parse_into_unit(content: &str) -> Result> { + let pairs = IDLParser::parse(Rule::schema, content)?; + let mut units = vec![]; + + for pair in pairs { units.push(parse_inner(pair).unwrap()) } + + Ok(units) +} + + +#[allow(unused)] +pub fn parse_inner(pairs: Pair) -> Result { + match pairs.as_rule() { + Rule::schema { + + }, + } +} +*/ + diff --git a/src/idl/pest.rs b/src/idl/pest.rs index 0fd42f2..e95aef2 100644 --- a/src/idl/pest.rs +++ b/src/idl/pest.rs @@ -3,7 +3,11 @@ // Local Uses use crate::ir::primitive; use crate::ir::primitive::TypeValue; -use crate::ir::unit::{Argument, Const, Direction, Enum, EnumVariant, Error, Field, Function, Parameter, Protocol, Settings, Struct, Unit}; +use crate::ir::unit::{ + Argument, Const, Direction, Enum, EnumVariant, + Error, Field, Function, Parameter, + Protocol, Settings, Struct, Unit +}; // External Uses use anyhow::Result; diff --git a/src/ir.rs b/src/ir.rs index 2e9cfc8..5b3a7c4 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -3,4 +3,6 @@ mod sanitizer; mod context; mod serialization; pub mod unit; +pub mod new_unit; pub mod primitive; +pub mod compiler; diff --git a/src/ir/compiler.rs b/src/ir/compiler.rs new file mode 100644 index 0000000..430335b --- /dev/null +++ b/src/ir/compiler.rs @@ -0,0 +1,7 @@ +// Standard Uses + +// Local Uses + +// External Uses + + diff --git a/src/ir/new_unit.rs b/src/ir/new_unit.rs new file mode 100644 index 0000000..74a2940 --- /dev/null +++ b/src/ir/new_unit.rs @@ -0,0 +1,78 @@ +// Standard Uses + +// Local Uses +use crate::ir::primitive::{Primitive, TypeValue}; + +// External Uses + + +pub type OrderIndex = u16; + +#[derive(Debug, Eq, PartialEq)] +pub enum Direction { Client, Server, Both } + + +#[derive(Debug, Eq, PartialEq)] +pub enum Unit { + Schema { + tag: String, + namespace: String, + imports: Vec, + constants: Vec, + settings: Vec, + enums: Vec, + structures: Vec, + validators: Vec, + errors: Vec, + protocols: Vec + }, + Constant { + name: String, + kind: String, + default_value: String, + }, + Enum { + name: String, + }, + Property { + name: String, + values: Vec + }, + Settings { + name: String, + parameters: Vec, + }, + Struct { + name: String, + parameters: Vec, + functions: Vec + }, + Protocol { + name: String, + parameters: Vec, + functions: Vec + }, + Function { + index: OrderIndex, + name: String, + synchronous: bool, + direction: Direction, + arguments: Vec, + returns: Vec, + throws: Vec + }, + Error { + name: String, + parameters: Vec, + fields: Vec + }, + Field { + index: OrderIndex, + + } +} + +pub enum UnitIndex { + Source(String), + +} diff --git a/src/ir/unit.rs b/src/ir/unit.rs index 262bc10..28726e2 100644 --- a/src/ir/unit.rs +++ b/src/ir/unit.rs @@ -19,7 +19,7 @@ pub enum Unit { Structs(Vec), Enums(Vec), Errors(Vec), - Protocols(Vec), + Protocols(Vec) } diff --git a/tests/idl/pest.rs b/tests/idl/pest.rs index 63336da..3b39560 100644 --- a/tests/idl/pest.rs +++ b/tests/idl/pest.rs @@ -3,13 +3,16 @@ // Local Uses // External Uses -use comline_rs::idl; -use comline_rs::ir::unit::Unit; +use comline::idl; +use comline::idl::constants::SCHEMA_EXTENSION; +use comline::ir::unit::Unit; #[test] fn from_raw_to_unit() { - let raw = std::fs::read_to_string("tests/idl/simple.cls").unwrap(); + let raw = std::fs::read_to_string( + format!("tests/idl/simple.{}", SCHEMA_EXTENSION) + ).unwrap(); let unit = idl::pest::parse_into_unit(raw.as_str()).unwrap(); assert_eq!( diff --git a/tests/idl/simple.cls b/tests/idl/simple.cls deleted file mode 100644 index 2488c95..0000000 --- a/tests/idl/simple.cls +++ /dev/null @@ -1,22 +0,0 @@ -namespace tests.idl.simple - -const DEFAULT_NAME: str = "flower" - -settings Test { - forbid_optional_indexing=True -} - -struct Message { - 1# name: str = DEFAULT_NAME - 2# optional recipient: str[validators=StringBounds(min_chars=3 max_chars=12)] = "bee" -} - -error RecipientNotFoundError { - message = "Recipient with name {self.recipient} not found" - 1# recipient: str[validators=StringBounds(min_chars=8 max_chars=16)] -} - -protocol Mail provider=Both { - 1# function send_message(message: Message) -> str ! NotFoundError[function.message.recipient] - : timeout_ms=1000 -} diff --git a/tests/idl/simple.ids b/tests/idl/simple.ids new file mode 100644 index 0000000..895327a --- /dev/null +++ b/tests/idl/simple.ids @@ -0,0 +1,31 @@ +namespace tests.idl.simple + +const POWER: u8 = 10 +const DEFAULT_NAME: str = f"flower {POWER}" + +settings Test { + forbid_optional_indexing=True +} + +struct Message { + 1# name: str = DEFAULT_NAME + + @validators=[StringBounds(min_chars=3 max_chars=12)] + 2# optional recipient: str = "bee" +} + +error RecipientNotFoundError { + message = f"Recipient with name {self.recipient} not found" + + @validators=[StringBounds(min_chars=8 max_chars=16)] + 1# recipient: str +} + + +@provider=Any +protocol Mail { + + @timeout_ms=1000 + 1# function send_message(message: Message) -> str + ! RecipientNotFoundError(function.message.recipient) +} diff --git a/tests/units/consts.cls b/tests/units/consts.ids similarity index 100% rename from tests/units/consts.cls rename to tests/units/consts.ids diff --git a/tests/units/dev.cls b/tests/units/dev.ids similarity index 76% rename from tests/units/dev.cls rename to tests/units/dev.ids index 5152589..5cd911e 100644 --- a/tests/units/dev.cls +++ b/tests/units/dev.ids @@ -4,14 +4,14 @@ const PROTOCOL: u8 = 0 const SECRET: u8 = 1 enum Motion { - 1 # Wave - 2 # MoveArm + 1# Wave + 2# MoveArm } struct Hello { const SHAKE: u8 = 20 - 1 # name: u8 + 1# name: u8 } struct Goodbye versioned=false { @@ -22,6 +22,6 @@ protocol Greet { function smile function wave () function wave_wait () -> u8 - function wave_and_go () -> str: timeout_ms=100 + function wave_and_go () -> str : timeout_ms=100 function wave_with (motion: Motion) } diff --git a/tests/units/mod.rs b/tests/units/mod.rs index 9c294a0..be4569d 100644 --- a/tests/units/mod.rs +++ b/tests/units/mod.rs @@ -5,8 +5,8 @@ use std::string::ToString; // External Uses use once_cell::sync::Lazy; -use comline_rs::ir::unit::{Const, Unit}; -use comline_rs::ir::primitive::TypeValue; +use comline::ir::unit::{Const, Unit}; +use comline::ir::primitive::TypeValue; // Unit with a namespace and a constant diff --git a/tests/units/protocol/args.cls b/tests/units/protocol/args.ids similarity index 100% rename from tests/units/protocol/args.cls rename to tests/units/protocol/args.ids diff --git a/tests/units/protocol/directions.cls b/tests/units/protocol/directions.cls deleted file mode 100644 index e69de29..0000000 diff --git a/tests/units/protocol/scalars.cls b/tests/units/protocol/scalars.cls deleted file mode 100644 index e69de29..0000000