From f6053e24e82b4cd01d2146527f8f878e251ca518 Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Mon, 2 Oct 2023 14:47:17 +0100 Subject: [PATCH] Parse to AST --- tools/lang/src/ast/errors.rs | 25 +- tools/lang/src/ast/mod.rs | 14 +- tools/lang/src/ast/nodes.rs | 159 ------------- tools/lang/src/ast/parse.rs | 440 +++++++++++++++++++++++++++++++++++ tools/lang/src/ast/types.rs | 290 ++++++++++++++++++++++- tools/lang/src/dscp.pest | 19 +- tools/lang/src/main.rs | 58 ++++- tools/lang/src/parser.rs | 2 +- 8 files changed, 804 insertions(+), 203 deletions(-) delete mode 100644 tools/lang/src/ast/nodes.rs create mode 100644 tools/lang/src/ast/parse.rs diff --git a/tools/lang/src/ast/errors.rs b/tools/lang/src/ast/errors.rs index eb22ef3c..54145737 100644 --- a/tools/lang/src/ast/errors.rs +++ b/tools/lang/src/ast/errors.rs @@ -1,29 +1,34 @@ use std::fmt; -use pest::Span; +use crate::{parser::Rule, CompilationStage}; -use crate::parser::Rule; +pub(crate) type PestError = pest::error::Error; +pub(crate) type ErrorVariant = pest::error::ErrorVariant; -pub struct AstBuildError<'a> { - pub(crate) message: String, - pub(crate) span: Span<'a> +pub struct AstBuildError { + pub(crate) stage: CompilationStage, + pub(crate) inner: PestError } -impl<'a> fmt::Display for AstBuildError<'a> { +impl fmt::Display for AstBuildError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "An Error Occurred, Please Try Again!") // user-facing output + write!(f, "Error occurred when {}: {}", self.stage, self.inner) // user-facing output } } -impl<'a> fmt::Debug for AstBuildError<'a> { +impl fmt::Debug for AstBuildError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{{ file: {}, line: {} }}", file!(), line!()) // programmer-facing output } } pub fn produce_unexpected_pair_error(pair: pest::iterators::Pair) -> Result { + let rule = pair.as_rule(); let span = pair.as_span(); let pair = pair.as_str().clone(); - let message = format!("Unexpected rule {}", pair); - Err(AstBuildError { message, span }) + let message = format!("Unexpected rule {:?} ({})", rule, pair); + Err(AstBuildError { + stage: CompilationStage::BuildAst, + inner: PestError::new_from_span(pest::error::ErrorVariant::CustomError { message }, span) + }) } diff --git a/tools/lang/src/ast/mod.rs b/tools/lang/src/ast/mod.rs index cb112ead..87663778 100644 --- a/tools/lang/src/ast/mod.rs +++ b/tools/lang/src/ast/mod.rs @@ -1,7 +1,9 @@ -pub mod errors; -pub mod nodes; -pub mod types; +mod errors; +mod parse; +mod types; -pub use errors::*; -pub use nodes::*; -pub use types::*; +pub(crate) use errors::*; + +pub use parse::parse_ast; + +pub(crate) use types::*; diff --git a/tools/lang/src/ast/nodes.rs b/tools/lang/src/ast/nodes.rs deleted file mode 100644 index 085de70b..00000000 --- a/tools/lang/src/ast/nodes.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::*; - -use crate::parser::Rule; - -fn parse_token_prop_type(pair: pest::iterators::Pair) -> Result, AstBuildError> { - let span = pair.as_span(); - let field_type = match pair.as_rule() { - Rule::file => Ok(TokenFieldType::File), - Rule::literal => Ok(TokenFieldType::Literal), - Rule::role => Ok(TokenFieldType::Role), - Rule::none => Ok(TokenFieldType::None), - Rule::literal_value => { - let literal_string = pair.into_inner().next().unwrap(); - let span = literal_string.as_span(); - let literal_val = literal_string.into_inner(); - Ok(TokenFieldType::LiteralValue(AstNode { - value: literal_val.as_str(), - span - })) - } - Rule::ident => Ok(TokenFieldType::Token(AstNode { - span: pair.as_span(), - value: pair.into_inner().as_str() - })), - _ => produce_unexpected_pair_error(pair) - }?; - - Ok(AstNode { - span, - value: field_type - }) -} - -fn parse_token_prop_field(pair: pest::iterators::Pair) -> Result, AstBuildError> { - match pair.as_rule() { - Rule::field => { - let span = pair.as_span(); - let mut pair = pair.into_inner(); - let name = pair.next().unwrap(); - let valid_types = pair - .next() - .unwrap() - .into_inner() - .into_iter() - .map(parse_token_prop_type) - .collect::, _>>()?; - Ok(AstNode { - span, - value: TokenProp { - name: AstNode { - value: name.as_str(), - span: name.as_span() - }, - types: valid_types - } - }) - } - _ => produce_unexpected_pair_error(pair) - } -} - -fn parse_token_props(pair: pest::iterators::Pair) -> Result>>, AstBuildError> { - let span = pair.as_span(); - match pair.as_rule() { - Rule::properties => Ok(AstNode { - span, - value: pair - .into_inner() - .into_iter() - .map(parse_token_prop_field) - .collect::, _>>()? - }), - _ => produce_unexpected_pair_error(pair) - } -} - -fn parse_token_decl(pair: pest::iterators::Pair) -> Result, AstBuildError> { - let span = pair.as_span(); - match pair.as_rule() { - Rule::struct_decl => { - let mut pair = pair.into_inner(); - let name = pair.next().unwrap(); - let props = parse_token_props(pair.next().unwrap())?; - - Ok(AstNode { - span, - value: TokenDecl { - name: AstNode { - value: name.as_str(), - span: name.as_span() - }, - props - } - }) - } - _ => produce_unexpected_pair_error(pair) - } -} - -fn parse_fn_decl(pair: pest::iterators::Pair) -> Result, AstBuildError> { - let span = pair.as_span(); - match pair.as_rule() { - Rule::fn_decl => { - let mut pair = pair.into_inner(); - let vis = pair.next().unwrap(); - let name = pair.next().unwrap(); - Ok(AstNode { - value: FnDecl { - visibility: AstNode { - value: match vis.as_str() { - "" => FnVis::Private, - "priv" => FnVis::Private, - "pub" => FnVis::Public, - _ => { - return Err(AstBuildError { - message: "visibility if specified must be pub/priv".into(), - span: vis.as_span() - }) - } - }, - span: vis.as_span() - }, - name: AstNode { - value: name.as_str(), - span: name.as_span() - } - }, - span - }) - } - _ => produce_unexpected_pair_error(pair) - } -} - -pub fn parse_ast(pairs: pest::iterators::Pairs) -> Result>, AstBuildError> { - pairs - .into_iter() - .filter_map(|pair| match pair.as_rule() { - Rule::struct_decl => { - let span = pair.as_span(); - let node = parse_token_decl(pair).map(|token_decl| AstNode { - span, - value: AstRoot::TokenDecl(token_decl) - }); - Some(node) - } - Rule::fn_decl => { - let fn_span = pair.as_span(); - let node = parse_fn_decl(pair).map(|fn_decl| AstNode { - span: fn_span, - value: AstRoot::FnDecl(fn_decl) - }); - Some(node) - } - Rule::EOI => None, - _ => panic!() - }) - .collect() -} diff --git a/tools/lang/src/ast/parse.rs b/tools/lang/src/ast/parse.rs new file mode 100644 index 00000000..2f9f66d1 --- /dev/null +++ b/tools/lang/src/ast/parse.rs @@ -0,0 +1,440 @@ +use super::*; + +use crate::parser::Rule; + +fn parse_token_prop_type(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + let field_type = match pair.as_rule() { + Rule::file => Ok(TokenFieldType::File), + Rule::literal => Ok(TokenFieldType::Literal), + Rule::role => Ok(TokenFieldType::Role), + Rule::none => Ok(TokenFieldType::None), + Rule::literal_value => { + let literal_string = pair.into_inner().next().unwrap(); + let span = literal_string.as_span(); + let literal_val = literal_string.into_inner(); + Ok(TokenFieldType::LiteralValue(AstNode { + value: literal_val.as_str(), + span + })) + } + Rule::ident => Ok(TokenFieldType::Token(AstNode { + span: pair.as_span(), + value: pair.as_str() + })), + _ => produce_unexpected_pair_error(pair) + }?; + + Ok(AstNode { + span, + value: field_type + }) +} + +fn parse_token_prop_field(pair: pest::iterators::Pair) -> Result, AstBuildError> { + match pair.as_rule() { + Rule::field => { + let span = pair.as_span(); + let mut pair = pair.into_inner(); + let name = pair.next().unwrap(); + let valid_types = pair + .next() + .unwrap() + .into_inner() + .into_iter() + .map(parse_token_prop_type) + .collect::, _>>()?; + Ok(AstNode { + span, + value: TokenPropDecl { + name: AstNode { + value: name.as_str(), + span: name.as_span() + }, + types: valid_types + } + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_token_props(pair: pest::iterators::Pair) -> Result>>, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::properties => Ok(AstNode { + span, + value: pair + .into_inner() + .into_iter() + .map(parse_token_prop_field) + .collect::, _>>()? + }), + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_token_decl(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::struct_decl => { + let mut pair = pair.into_inner(); + let name = pair.next().unwrap(); + let props = parse_token_props(pair.next().unwrap())?; + + Ok(AstNode { + span, + value: TokenDecl { + name: AstNode { + value: name.as_str(), + span: name.as_span() + }, + props + } + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_fn_arg(pair: pest::iterators::Pair) -> Result, AstBuildError> { + match pair.as_rule() { + Rule::fn_decl_arg => { + let span = pair.as_span(); + let mut pairs = pair.into_inner(); + let name = pairs.next().unwrap(); + let token_type = pairs.next().unwrap(); + Ok(AstNode { + value: FnArg { + name: AstNode { + value: name.as_str(), + span: name.as_span() + }, + token_type: AstNode { + value: token_type.as_str(), + span: token_type.as_span() + } + }, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_fn_decl_args(pair: pest::iterators::Pair) -> Result>>, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::fn_decl_arg_list => { + let args = pair.into_inner(); + Ok(AstNode { + value: args.into_iter().map(parse_fn_arg).collect::, _>>()?, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_fn_vis(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::vis => Ok(AstNode { + value: match pair.as_str() { + "" => FnVis::Private, + "priv" => FnVis::Private, + "pub" => FnVis::Public, + _ => { + return Err(AstBuildError { + stage: crate::CompilationStage::BuildAst, + inner: PestError::new_from_span( + ErrorVariant::CustomError { + message: "visibility if specified must be pub/priv".into() + }, + span + ) + }) + } + }, + span + }), + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_bool_cmp_op(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::eq => Ok(AstNode { + value: BoolCmp::Eq, + span + }), + Rule::neq => Ok(AstNode { + value: BoolCmp::Neq, + span + }), + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_type_cmp_op(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::is => Ok(AstNode { + value: TypeCmp::Is, + span + }), + Rule::isnt => Ok(AstNode { + value: TypeCmp::Isnt, + span + }), + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_ident_prop<'a>(pair: pest::iterators::Pair<'a, Rule>) -> Result>, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::ident_prop => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: TokenProp { + token: pairs.next().unwrap().as_str(), + prop: pairs.next().unwrap().as_str() + }, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_fn_inv_args<'a>( + pair: pest::iterators::Pair +) -> Result>>, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::fn_args => { + let args = pair.into_inner(); + Ok(AstNode { + value: args + .into_iter() + .map(|p| AstNode { + value: p.as_str(), + span: p.as_span() + }) + .collect::>(), + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_bool_cmp(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let cmp_type = pair.as_rule(); + let span = pair.as_span(); + match cmp_type { + Rule::fn_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::Fn { + name: pairs.next().unwrap().as_str(), + inputs: parse_fn_inv_args(pairs.next().unwrap())?, + outputs: parse_fn_inv_args(pairs.next().unwrap())? + }, + span + }) + } + Rule::prop_prop_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::PropProp { + left: parse_ident_prop(pairs.next().unwrap())?, + op: parse_bool_cmp_op(pairs.next().unwrap())?, + right: parse_ident_prop(pairs.next().unwrap())? + }, + span + }) + } + Rule::prop_lit_cmp => { + let mut pairs = pair.into_inner(); + + let left = parse_ident_prop(pairs.next().unwrap())?; + let op = parse_bool_cmp_op(pairs.next().unwrap())?; + let right = pairs.next().unwrap(); // literal_value + let right = right.into_inner().next().unwrap(); // string + let right = right.into_inner().next().unwrap().as_str(); // inner + + Ok(AstNode { + value: Comparison::PropLit { left, op, right }, + span + }) + } + Rule::prop_sender_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::PropSender { + left: parse_ident_prop(pairs.next().unwrap())?, + op: parse_bool_cmp_op(pairs.next().unwrap())? + }, + span + }) + } + Rule::prop_ident_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::PropToken { + left: parse_ident_prop(pairs.next().unwrap())?, + op: parse_bool_cmp_op(pairs.next().unwrap())?, + right: pairs.next().unwrap().as_str() + }, + span + }) + } + Rule::ident_ident_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::TokenToken { + left: pairs.next().unwrap().as_str(), + op: parse_bool_cmp_op(pairs.next().unwrap())?, + right: pairs.next().unwrap().as_str() + }, + span + }) + } + Rule::prop_type_cmp => { + let mut pairs = pair.into_inner(); + Ok(AstNode { + value: Comparison::PropType { + left: parse_ident_prop(pairs.next().unwrap())?, + op: parse_type_cmp_op(pairs.next().unwrap())?, + right: pairs.next().unwrap().as_str() + }, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn partition_pairs_by_op<'a, I>(pairs: I, rule: Rule, op: BoolOp) -> Result>, AstBuildError> +where + I: IntoIterator>, + ::Item: Clone, + ::IntoIter: Clone +{ + let mut pairs = pairs.into_iter(); + let left: Vec<_> = pairs.by_ref().take_while(|p| p.as_rule() != rule.clone()).collect(); + let right: Vec<_> = pairs.collect(); + + match right.len() != 0 { + true => Ok(Some(ExpressionTree::Node { + left: Box::new(parse_exp_tree(left)?), + op, + right: Box::new(parse_exp_tree(right)?) + })), + false => Ok(None) + } +} + +fn parse_exp_tree<'a, I>(pairs: I) -> Result, AstBuildError> +where + I: IntoIterator>, + ::Item: Clone, + ::IntoIter: Clone +{ + let mut pairs = pairs.into_iter(); + let split = partition_pairs_by_op(pairs.clone(), Rule::bool_op_and, BoolOp::And)?; + if split.is_some() { + return Ok(split.unwrap()); + } + let split = partition_pairs_by_op(pairs.clone(), Rule::bool_op_xor, BoolOp::Xor)?; + if split.is_some() { + return Ok(split.unwrap()); + } + let split = partition_pairs_by_op(pairs.clone(), Rule::bool_op_or, BoolOp::Or)?; + if split.is_some() { + return Ok(split.unwrap()); + } + + let pair = pairs.next().unwrap(); + match pair.as_rule() { + Rule::unary_not => Ok(ExpressionTree::Not(Box::new(parse_exp_tree( + pairs.next().unwrap().into_inner() + )?))), + Rule::expr => parse_exp_tree(pair.into_inner()), + _ => Ok(ExpressionTree::Leaf(parse_bool_cmp(pair)?)) + } +} + +fn parse_fn_conditions(pair: pest::iterators::Pair) -> Result>, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::expr_list => { + let inner = pair.into_inner(); + Ok(AstNode { + value: inner + .into_iter() + .map(|pair| parse_exp_tree(Box::new(pair.into_inner()))) + .collect::, _>>()?, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +fn parse_fn_decl(pair: pest::iterators::Pair) -> Result, AstBuildError> { + let span = pair.as_span(); + match pair.as_rule() { + Rule::fn_decl => { + let mut pair = pair.into_inner(); + let vis = pair.next().unwrap(); + let name = pair.next().unwrap(); + let inputs = pair.next().unwrap(); + let outputs = pair.next().unwrap(); + let conditions = pair.next().unwrap(); + Ok(AstNode { + value: FnDecl { + visibility: parse_fn_vis(vis)?, + name: AstNode { + value: name.as_str(), + span: name.as_span() + }, + inputs: parse_fn_decl_args(inputs)?, + outputs: parse_fn_decl_args(outputs)?, + conditions: parse_fn_conditions(conditions)? + }, + span + }) + } + _ => produce_unexpected_pair_error(pair) + } +} + +pub fn parse_ast(pairs: pest::iterators::Pairs) -> Result>, AstBuildError> { + pairs + .into_iter() + .filter_map(|pair| match pair.as_rule() { + Rule::struct_decl => { + let span = pair.as_span(); + let node = parse_token_decl(pair).map(|token_decl| AstNode { + span, + value: AstRoot::TokenDecl(token_decl) + }); + Some(node) + } + Rule::fn_decl => { + let fn_span = pair.as_span(); + let node = parse_fn_decl(pair).map(|fn_decl| AstNode { + span: fn_span, + value: AstRoot::FnDecl(fn_decl) + }); + Some(node) + } + Rule::EOI => None, + _ => panic!() + }) + .collect() +} diff --git a/tools/lang/src/ast/types.rs b/tools/lang/src/ast/types.rs index 172570a0..578d03ff 100644 --- a/tools/lang/src/ast/types.rs +++ b/tools/lang/src/ast/types.rs @@ -1,13 +1,36 @@ -use pest::Span; +use std::fmt::{Debug, Display}; -use super::*; +use pest::Span; -#[derive(Debug)] -pub struct AstNode<'a, V> { +pub struct AstNode<'a, V> +where + V: 'a +{ pub(crate) value: V, pub(crate) span: Span<'a> } +impl<'a, V> Display for AstNode<'a, V> +where + V: Display +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl<'a, V> Debug for AstNode<'a, V> +where + V: Debug +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AstNode") + .field("value", &self.value) + .field("span", &self.span) + .finish() + } +} + #[derive(Debug)] pub enum TokenFieldType<'a> { None, @@ -18,16 +41,60 @@ pub enum TokenFieldType<'a> { Token(AstNode<'a, &'a str>) } +impl<'a> Display for TokenFieldType<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TokenFieldType::None => write!(f, "None"), + TokenFieldType::File => write!(f, "File"), + TokenFieldType::Role => write!(f, "Role"), + TokenFieldType::Literal => write!(f, "Literal"), + TokenFieldType::LiteralValue(s) => write!(f, "\"{}\"", s.value), + TokenFieldType::Token(s) => write!(f, "{}", s.value) + } + } +} + #[derive(Debug)] -pub struct TokenProp<'a> { +pub struct TokenPropDecl<'a> { pub(crate) name: AstNode<'a, &'a str>, pub(crate) types: Vec>> } +impl<'a> Display for TokenPropDecl<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}: {}", + self.name, + self.types + .iter() + .map(|t| format!("{}", t)) + .collect::>() + .join(" | ") + ) + } +} + #[derive(Debug)] pub struct TokenDecl<'a> { pub(crate) name: AstNode<'a, &'a str>, - pub(crate) props: AstNode<'a, Vec>>> + pub(crate) props: AstNode<'a, Vec>>> +} + +impl<'a> Display for TokenDecl<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "token {} {{\n{}\n}}", + self.name, + self.props + .value + .iter() + .map(|p| format!("\t{}", p)) + .collect::>() + .join("\n") + ) + } } #[derive(Debug)] @@ -36,10 +103,219 @@ pub enum FnVis { Public } +impl Display for FnVis { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + &FnVis::Public => write!(f, "pub"), + &FnVis::Private => write!(f, "priv") + } + } +} + +#[derive(Debug)] +pub struct FnArg<'a> { + pub(crate) name: AstNode<'a, &'a str>, + pub(crate) token_type: AstNode<'a, &'a str> +} + +impl<'a> Display for FnArg<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.name, self.token_type) + } +} + +#[derive(Debug)] +pub enum BoolOp { + And, + Or, + Xor +} +#[derive(Debug)] +pub enum BoolCmp { + Eq, + Neq +} +#[derive(Debug)] +pub enum TypeCmp { + Is, + Isnt +} + +#[derive(Debug)] +pub struct TokenProp<'a> { + pub(crate) token: &'a str, + pub(crate) prop: &'a str +} + +#[derive(Debug)] +pub enum Comparison<'a> { + Fn { + name: &'a str, + inputs: AstNode<'a, Vec>>, + outputs: AstNode<'a, Vec>> + }, + PropLit { + left: AstNode<'a, TokenProp<'a>>, + op: AstNode<'a, BoolCmp>, + right: &'a str + }, + PropSender { + left: AstNode<'a, TokenProp<'a>>, + op: AstNode<'a, BoolCmp> + }, + TokenToken { + left: &'a str, + op: AstNode<'a, BoolCmp>, + right: &'a str + }, + PropToken { + left: AstNode<'a, TokenProp<'a>>, + op: AstNode<'a, BoolCmp>, + right: &'a str + }, + PropProp { + left: AstNode<'a, TokenProp<'a>>, + op: AstNode<'a, BoolCmp>, + right: AstNode<'a, TokenProp<'a>> + }, + PropType { + left: AstNode<'a, TokenProp<'a>>, + op: AstNode<'a, TypeCmp>, + right: &'a str + } +} + +impl<'a> Display for Comparison<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Comparison::Fn { name, inputs, outputs } => { + let inputs = inputs.value.iter().map(|i| i.value).collect::>().join(", "); + let outputs = outputs.value.iter().map(|i| i.value).collect::>().join(", "); + write!(f, "{} |{}| => |{}|", name, inputs, outputs) + } + Comparison::PropLit { left, op, right } => { + let op = match op.value { + BoolCmp::Eq => "==", + BoolCmp::Neq => "!=" + }; + write!(f, "{}.{} {} \"{}\"", left.value.token, left.value.prop, op, right) + } + Comparison::PropSender { left, op } => { + let op = match op.value { + BoolCmp::Eq => "==", + BoolCmp::Neq => "!=" + }; + write!(f, "{}.{} {} sender", left.value.token, left.value.prop, op) + } + Comparison::TokenToken { left, op, right } => { + let op = match op.value { + BoolCmp::Eq => "==", + BoolCmp::Neq => "!=" + }; + write!(f, "{} {} {}", left, op, right) + } + Comparison::PropToken { left, op, right } => { + let op = match op.value { + BoolCmp::Eq => "==", + BoolCmp::Neq => "!=" + }; + write!(f, "{}.{} {} {}", left.value.token, left.value.prop, op, right) + } + Comparison::PropProp { left, op, right } => { + let op = match op.value { + BoolCmp::Eq => "==", + BoolCmp::Neq => "!=" + }; + write!( + f, + "{}.{} {} {}.{}", + left.value.token, left.value.prop, op, right.value.token, right.value.prop + ) + } + Comparison::PropType { left, op, right } => { + let op = match op.value { + TypeCmp::Is => ":", + TypeCmp::Isnt => "!:" + }; + write!(f, "{}.{}{} {}", left.value.token, left.value.prop, op, right) + } + } + } +} + +#[derive(Debug)] +pub enum ExpressionTree<'a> { + Leaf(AstNode<'a, Comparison<'a>>), + Not(Box>), + Node { + left: Box>, + op: BoolOp, + right: Box> + } +} + +impl<'a> Display for ExpressionTree<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ExpressionTree::Leaf(c) => write!(f, "{}", c), + ExpressionTree::Not(e) => write!(f, "!({})", e), + ExpressionTree::Node { left, op, right } => { + let op_symbol = match op { + BoolOp::And => "&", + BoolOp::Xor => "^", + BoolOp::Or => "|" + }; + write!(f, "({} {} {})", left, op_symbol, right) + } + } + } +} + #[derive(Debug)] pub struct FnDecl<'a> { pub(crate) visibility: AstNode<'a, FnVis>, - pub(crate) name: AstNode<'a, &'a str> + pub(crate) name: AstNode<'a, &'a str>, + pub(crate) inputs: AstNode<'a, Vec>>>, + pub(crate) outputs: AstNode<'a, Vec>>>, + pub(crate) conditions: AstNode<'a, Vec>> +} + +fn format_fn_args(args: Vec<&FnArg>) -> String { + match args.len() < 3 { + true => format!( + "|{}|", + args.iter().map(|v| format!("{}", v)).collect::>().join(", ") + ), + false => format!( + "|\n{}\n|", + args.iter().map(|v| format!("\t{}", v)).collect::>().join("\n") + ) + } +} + +fn format_conditions_list(conditions: Vec<&ExpressionTree>) -> String { + format!( + "{{\n{}\n}}", + conditions + .iter() + .map(|v| format!("\t{},", v)) + .collect::>() + .join("\n") + ) +} + +impl<'a> Display for FnDecl<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} fn {} {} => {} where {}", + self.visibility, + self.name, + format_fn_args(self.inputs.value.iter().map(|v| &v.value).collect()), + format_fn_args(self.outputs.value.iter().map(|v| &v.value).collect()), + format_conditions_list(self.conditions.value.iter().collect::>()) + ) + } } #[derive(Debug)] diff --git a/tools/lang/src/dscp.pest b/tools/lang/src/dscp.pest index df286ebe..9a51771d 100644 --- a/tools/lang/src/dscp.pest +++ b/tools/lang/src/dscp.pest @@ -33,26 +33,26 @@ properties = { (field ~ ",")* ~ (field)? } struct_decl = { "struct" ~ ident ~ "{" ~ properties ~ "}" } -prop_lit_cmp = { ident_prop ~ cmp_op ~ (sender | literal_value) } +prop_lit_cmp = { ident_prop ~ cmp_op ~ literal_value } +prop_sender_cmp = { ident_prop ~ cmp_op ~ sender } ident_ident_cmp = { ident ~ cmp_op ~ ident } prop_ident_cmp = { ident_prop ~ cmp_op ~ ident } prop_prop_cmp = { ident_prop ~ cmp_op ~ ident_prop } prop_type_cmp = { ident_prop ~ cmp_type_op ~ cmp_type } -val = { ident_prop | ident } -fn_args = { "|" ~ (val ~ ",")* ~ val? ~ "|" } +fn_args = { "|" ~ (ident ~ ",")* ~ ident? ~ "|" } fn_cmp = { ident ~ fn_args ~ "=>" ~ fn_args } -cmp = { fn_cmp | prop_prop_cmp | prop_lit_cmp | prop_ident_cmp | ident_ident_cmp | prop_type_cmp } +cmp = _{ fn_cmp | prop_prop_cmp | prop_lit_cmp | prop_sender_cmp | prop_ident_cmp | ident_ident_cmp | prop_type_cmp } -cmp_op = @{ eq | neq } +cmp_op = _{ eq | neq } eq = { "==" } neq = { "!=" } -cmp_type_op = @{ is | isnt } +cmp_type_op = _{ is | isnt } is = { ":" } isnt = { "!:" } -bool_op = { bool_op_and | bool_op_or | bool_op_xor } +bool_op = _{ bool_op_and | bool_op_or | bool_op_xor } bool_op_and = { "&" | ^"and" } bool_op_or = { "|" | ^"or" } bool_op_xor = { "^" | ^"xor" } @@ -65,8 +65,9 @@ expr = { atom ~ (bool_op ~ atom)* } expr_list = { "{" ~ (expr ~ ",")* ~ expr? ~ "}" } vis = { (pub | priv)? } -arg_list = { "|" ~ properties? ~ "|" } -fn_decl = { vis ~ fn ~ ident ~ arg_list ~ "=>" ~ arg_list ~ where ~ expr_list } +fn_decl_arg = { ident ~ ":" ~ ident } +fn_decl_arg_list = { "|" ~ (fn_decl_arg ~ ",")* ~ (fn_decl_arg)? ~ "|" } +fn_decl = { vis ~ fn ~ ident ~ fn_decl_arg_list ~ "=>" ~ fn_decl_arg_list ~ where ~ expr_list } decl = _{ struct_decl | fn_decl } program = _{ decl+ } diff --git a/tools/lang/src/main.rs b/tools/lang/src/main.rs index f53cef22..078fac6f 100644 --- a/tools/lang/src/main.rs +++ b/tools/lang/src/main.rs @@ -1,29 +1,65 @@ -use pest::{Parser, Span}; -use std::{env, fmt, fs, io}; +use pest::Parser; +use std::{env, fmt::Display, fs, io}; pub mod parser; use parser::*; pub mod ast; +pub use ast::parse_ast; use ast::*; +#[derive(Debug)] +pub enum CompilationStage { + ParseGrammar, + BuildAst +} + +impl Display for CompilationStage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CompilationStage::ParseGrammar => write!(f, "parsing grammar"), + CompilationStage::BuildAst => write!(f, "building ast") + } + } +} + +pub fn parse_str_to_ast(input: &str) -> Result>, AstBuildError> { + let pairs = DscpParser::parse(Rule::main, input); + if let Err(e) = pairs { + return Err(AstBuildError { + stage: CompilationStage::ParseGrammar, + inner: e + }); + } + let pairs = pairs.unwrap(); + + parse_ast(pairs) +} + fn main() -> io::Result<()> { let file_path = env::args().nth(1).unwrap(); let contents = fs::read_to_string(file_path)?; - let pairs = DscpParser::parse(Rule::main, &contents); - if let Err(e) = pairs { - eprintln!("Parse failed: {:?}", e); + let ast = parse_str_to_ast(&contents); + + if let Err(e) = ast { + eprintln!("{}", e); return Ok(()); } - let pairs = pairs.unwrap(); - let ast = parse_ast(pairs); + let ast = ast.unwrap(); + let token_decls = ast.iter().filter_map(|decl| match &decl.value { + AstRoot::TokenDecl(t) => Some(&t.value), + AstRoot::FnDecl(_) => None + }); + + let fn_decls = ast.iter().filter_map(|decl| match &decl.value { + AstRoot::TokenDecl(_) => None, + AstRoot::FnDecl(f) => Some(&f.value) + }); - match ast { - Ok(ast) => println!("{:?}", ast), - Err(e) => eprintln!("Error occurred {}", e) - }; + token_decls.for_each(|t| println!("{}\n", t)); + fn_decls.for_each(|f| println!("{}\n", f)); Ok(()) } diff --git a/tools/lang/src/parser.rs b/tools/lang/src/parser.rs index a1f2162e..b997f1d0 100644 --- a/tools/lang/src/parser.rs +++ b/tools/lang/src/parser.rs @@ -1,3 +1,3 @@ #[derive(pest_derive::Parser)] #[grammar = "dscp.pest"] -pub struct DscpParser; +pub(crate) struct DscpParser;