Skip to content

Commit

Permalink
feat: add struct parse & ast support
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Sep 30, 2022
1 parent 8c4ca03 commit bf954a7
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 29 deletions.
32 changes: 24 additions & 8 deletions crates/fkl_parser/src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub enum FklDeclaration {
ValueObject(ValueObjectDecl),
Implementation(ImplementationDecl),
Component(ComponentDecl),
Struct(StructDecl),
DomainService(DomainServiceDecl),
ApplicationService(ApplicationServiceDecl),
}
Expand Down Expand Up @@ -136,6 +137,13 @@ pub struct AggregateDecl {
pub value_objects: Vec<ValueObjectDecl>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct StructDecl {
pub name: String,
pub inline_doc: String,
pub fields: Vec<VariableDefinition>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct UsedDomainObject {
pub name: String,
Expand Down Expand Up @@ -192,7 +200,7 @@ pub struct ImplementationDecl {
pub inline_doc: String,
pub qualified_name: String,
// can be file path or url
pub endpoint: Vec<EndpointDecl>,
pub endpoints: Vec<EndpointDecl>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
Expand All @@ -205,21 +213,28 @@ pub struct SourceSetDecl {
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EndpointDecl {
pub name: String,
pub inline_doc: String,
pub method: String,
pub path: String,
pub request: Option<RequestDecl>,
pub response: Option<ResponseDecl>,
pub uri: String,
pub authorization: Option<AuthorizationDecl>,
pub request: Option<HttpRequestDecl>,
pub response: Option<HttpResponseDecl>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct RequestDecl {

pub struct AuthorizationDecl {
pub authorization_type: String,
pub username: Option<String>,
pub password: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ResponseDecl {
pub struct HttpRequestDecl {

}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HttpResponseDecl {
pub(crate) name: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -273,6 +288,7 @@ pub struct ComponentDecl {
pub name: String,
pub component_type: ComponentType,
pub inline_doc: String,
pub used_domain_objects: Vec<UsedDomainObject>,
pub attributes: Vec<AttributeDefinition>,
}

Expand Down
19 changes: 12 additions & 7 deletions crates/fkl_parser/src/parser/fkl.pest
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ declaration = {
| value_object_decl
| struct_decl
| component_decl
| impl_decl
| implementation_decl
}

context_map_decl = {
Expand Down Expand Up @@ -96,20 +96,20 @@ struct_decl = {
("struct" | "Struct") ~ identifier? ~ "{" ~ fields_decl ~ "}"
}

impl_decl = {
implementation_decl = {
"impl" ~ identifier ~ "{" ~ (inline_doc | endpoint_decl)? ~ "}"
}

endpoint_decl = {
"endpoint" ~ "{" ~ http_action ~ authorization_decl? ~ http_response_decl? ~ "}"
"endpoint" ~ "{" ~ http_request_decl ~ authorization_decl? ~ http_response_decl? ~ "}"
}

http_response_decl = {
"response" ~ ":" ~ identifier ~ ";"?
}

authorization_decl = {
"authorization" ~ ":" ~ authorization_type ~ special_string ~ special_string? ~ ";"?
"authorization" ~ ":" ~ authorization_type ~ username? ~ password? ~ ";"?
}

authorization_type = {
Expand All @@ -119,8 +119,12 @@ authorization_type = {
username = { special_string }
password = { special_string }

http_action = {
("GET" | "POST" | "PUT" | "DELETE" | "PATCH") ~ custom_param? ~ uri ~ http_body? ~ ";"?
http_request_decl = {
http_method ~ custom_param? ~ uri ~ http_body? ~ ";"?
}

http_method = {
"GET" | "POST" | "PUT" | "DELETE" | "PATCH"
}

custom_param = {
Expand All @@ -141,7 +145,7 @@ host = {
}

port = { number }
path = { special_string }
path = { uri_string }
query = { "?" ~ special_string ~ ("&" ~ special_string)* }
frag = { "#" ~ special_string }

Expand Down Expand Up @@ -228,6 +232,7 @@ string = @{
number = @{ '0'..'9'+ }
int = @{ number | "-" ~ "0"* ~ '1'..'9' ~ number? }
special_string = @{ (ASCII_ALPHANUMERIC | "-" | "_" )* }
uri_string = @{ (ASCII_ALPHANUMERIC | "-" | "_" | "{" | "}" )* }

inline_doc = {
"\"\"\"" ~ (inline_doc | !"\"\"\"" ~ ANY)* ~ "\"\"\""
Expand Down
165 changes: 151 additions & 14 deletions crates/fkl_parser/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;

use pest::iterators::{Pair, Pairs};

use crate::parser::ast::{AggregateDecl, AttributeDefinition, BoundedContextDecl, ComponentDecl, ContextMapDecl, ContextRelation, EntityDecl, FklDeclaration, Identifier, Loc, RelationDirection, UsedDomainObject, ValueObjectDecl, VariableDefinition};
use crate::parser::ast::{AggregateDecl, AttributeDefinition, AuthorizationDecl, BoundedContextDecl, ComponentDecl, ContextMapDecl, ContextRelation, EndpointDecl, EntityDecl, FklDeclaration, HttpResponseDecl, Identifier, ImplementationDecl, Loc, RelationDirection, StructDecl, UsedDomainObject, ValueObjectDecl, VariableDefinition};
use crate::parser::parse_result::{ParseError, ParseResult};
use crate::pest::Parser;

Expand Down Expand Up @@ -53,6 +53,12 @@ fn consume_declarations(pairs: Pairs<Rule>) -> Vec<FklDeclaration> {
Rule::value_object_decl => {
decl = FklDeclaration::ValueObject(consume_value_object(p));
}
Rule::implementation_decl => {
decl = FklDeclaration::Implementation(consume_implementation(p));
}
Rule::struct_decl => {
decl = FklDeclaration::Struct(consume_struct(p));
}
_ => println!("unreachable content rule: {:?}", p.as_rule())
};
}
Expand Down Expand Up @@ -277,21 +283,27 @@ fn consume_struct_decl(pair: Pair<Rule>) -> Vec<VariableDefinition> {
for p in pair.into_inner() {
match p.as_rule() {
Rule::fields_decl => {
for p in p.into_inner() {
match p.as_rule() {
Rule::name_type_def => {
fields.push(consume_parameter(p));
}
_ => println!("unreachable struct_decl rule: {:?}", p.as_rule())
}
}
fields = consume_fields_decl(p);
}
_ => println!("unreachable struct rule: {:?}", p.as_rule())
};
}
return fields;
}

fn consume_fields_decl(pair: Pair<Rule>) -> Vec<VariableDefinition> {
let mut fields: Vec<VariableDefinition> = vec![];
for p in pair.into_inner() {
match p.as_rule() {
Rule::name_type_def => {
fields.push(consume_parameter(p));
}
_ => println!("unreachable fields rule: {:?}", p.as_rule())
};
}
return fields;
}

fn consume_parameter(pair: Pair<Rule>) -> VariableDefinition {
let mut field = VariableDefinition::default();
for p in pair.into_inner() {
Expand Down Expand Up @@ -343,6 +355,9 @@ fn consume_component(pair: Pair<Rule>) -> ComponentDecl {
Rule::attr_decl => {
component.attributes.push(consume_attribute(p));
}
Rule::used_domain_objects_decl => {
component.used_domain_objects = [component.used_domain_objects, consume_use_domain_object(p)].concat();
}
_ => println!("unreachable component rule: {:?}", p.as_rule())
};
}
Expand Down Expand Up @@ -381,6 +396,108 @@ fn consume_attr_value(pair: Pair<Rule>) -> String {
return value;
}

fn consume_implementation(pair: Pair<Rule>) -> ImplementationDecl {
let mut implementation = ImplementationDecl::default();
for p in pair.into_inner() {
match p.as_rule() {
Rule::identifier => {
implementation.name = p.as_str().to_string();
}
Rule::inline_doc => {
implementation.inline_doc = parse_inline_doc(p);
}
Rule::endpoint_decl => {
implementation.endpoints.push(consume_endpoint(p));
}
_ => println!("unreachable implementation rule: {:?}", p.as_rule())
};
}
return implementation;
}

fn consume_endpoint(pair: Pair<Rule>) -> EndpointDecl {
let mut endpoint = EndpointDecl::default();
for p in pair.into_inner() {
match p.as_rule() {
Rule::identifier => {
endpoint.name = p.as_str().to_string();
}
Rule::http_request_decl => {
for inner in p.into_inner() {
match inner.as_rule() {
Rule::http_method => {
endpoint.method = inner.as_str().to_string();
}
Rule::uri => {
endpoint.uri = inner.as_str().to_string();
}
_ => println!("unreachable http_request_decl rule: {:?}", inner.as_rule())
}
}
}
Rule::authorization_decl => {
endpoint.authorization = Some(consume_authorization(p));
}
Rule::http_response_decl => {
endpoint.response = Some(consume_http_response(p));
}
_ => println!("unreachable endpoint rule: {:?}", p.as_rule())
};
}
return endpoint;
}

fn consume_struct(pair: Pair<Rule>) -> StructDecl {
let mut struct_decl = StructDecl::default();
for p in pair.into_inner() {
match p.as_rule() {
Rule::identifier => {
struct_decl.name = p.as_str().to_string();
}
Rule::inline_doc => {
struct_decl.inline_doc = parse_inline_doc(p);
}
Rule::fields_decl => {
struct_decl.fields = consume_fields_decl(p);
}
_ => println!("unreachable struct rule: {:?}", p.as_rule())
};
}
return struct_decl;
}

fn consume_authorization(pair: Pair<Rule>) -> AuthorizationDecl {
let mut authorization = AuthorizationDecl::default();
for p in pair.into_inner() {
match p.as_rule() {
Rule::authorization_type => {
authorization.authorization_type = p.as_str().to_string();
}
Rule::username => {
authorization.username = Some(p.as_str().to_string());
}
Rule::password => {
authorization.password = Some(p.as_str().to_string());
}
_ => println!("unreachable authorization rule: {:?}", p.as_rule())
};
}
return authorization;
}

fn consume_http_response(pair: Pair<Rule>) -> HttpResponseDecl {
let mut response = HttpResponseDecl::default();
for p in pair.into_inner() {
match p.as_rule() {
Rule::identifier => {
response.name = p.as_str().to_string();
}
_ => println!("unreachable http_response rule: {:?}", p.as_rule())
};
}
return response;
}

fn parse_string(str: &str) -> String {
let mut s = str.to_string();
s.remove(0);
Expand Down Expand Up @@ -729,6 +846,9 @@ Component SalesComponent {
value: "Application".to_string(),
},
],
used_domain_objects: vec![
UsedDomainObject { name: "SalesOrder".to_string() },
],
}));
}

Expand Down Expand Up @@ -833,13 +953,10 @@ Aggregate Cinema {

#[test]
fn aggregate_binding_syntax() {
let result = parse(r#"Context Cinema {
DomainEvent CinemaCreated(impl = CinemaCreatedEvent);
}
let result = parse(r#"
impl CinemaCreatedEvent {
endpoint {
GET ${uri}/post;
GET /book/{id};
authorization: Basic admin admin;
response: Cinema;
}
Expand All @@ -853,5 +970,25 @@ struct Cinema {
}
"#).unwrap();

assert_eq!(result[0], FklDeclaration::Implementation(ImplementationDecl {
name: "CinemaCreatedEvent".to_string(),
inline_doc: "".to_string(),
qualified_name: "".to_string(),
endpoints: vec![
EndpointDecl {
name: "".to_string(),
method: "GET".to_string(),
uri: "/book/{id}".to_string(),
authorization: Some(AuthorizationDecl {
authorization_type: "Basic".to_string(),
username: Some("admin".to_string()),
password: Some("admin".to_string()),
}),
request: None,
response: Some(HttpResponseDecl {
name: "Cinema".to_string()
}),
}],
}));
}
}

0 comments on commit bf954a7

Please sign in to comment.