use super::span::SpanInfo;
use crate::format;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstTrivia {
Whitespace { content: String, span: SpanInfo },
LineComment {
content: String, span: SpanInfo,
},
BlockComment {
content: String, span: SpanInfo,
},
}
impl CstTrivia {
pub fn span(&self) -> &SpanInfo {
match self {
Self::Whitespace { span, .. } => span,
Self::LineComment { span, .. } => span,
Self::BlockComment { span, .. } => span,
}
}
pub fn content(&self) -> &str {
match self {
Self::Whitespace { content, .. } => content,
Self::LineComment { content, .. } => content,
Self::BlockComment { content, .. } => content,
}
}
pub fn has_newline(&self) -> bool {
self.content().contains('\n')
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstRoot {
pub name: String,
pub nodes: Vec<CstNode>,
pub span: SpanInfo,
}
impl CstRoot {
pub fn to_ast(&self) -> crate::error::Result<crate::format::Story> {
let mut paragraphs = Vec::new();
for node in &self.nodes {
if let CstNode::Paragraph(para) = node {
paragraphs.push(para.to_ast()?);
}
}
Ok(crate::format::Story {
name: self.name.clone(),
paragraphs,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstNode {
Trivia(CstTrivia),
Paragraph(CstParagraph),
Command(CstCommand),
SystemCall(CstSystemCall),
TextLine(CstTextLine),
Block(CstBlock),
EmbeddedCode(CstEmbeddedCode),
Attribute(CstAttribute),
Error {
content: String,
span: SpanInfo,
message: String,
},
}
impl CstNode {
pub fn span(&self) -> SpanInfo {
match self {
Self::Trivia(t) => *t.span(),
Self::Paragraph(p) => p.span,
Self::Command(c) => c.span,
Self::SystemCall(s) => s.span,
Self::TextLine(t) => t.span,
Self::Block(b) => b.span,
Self::EmbeddedCode(e) => e.span,
Self::Attribute(a) => a.span,
Self::Error { span, .. } => *span,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstAttribute {
pub keyword: String,
pub keyword_span: SpanInfo,
pub condition: Option<String>,
pub condition_span: Option<SpanInfo>,
pub open_token: SpanInfo,
pub close_token: SpanInfo,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
}
impl CstAttribute {
pub fn to_ast(&self) -> format::Attribute {
format::Attribute {
keyword: self.keyword.clone(),
condition: self.condition.clone(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CommandSyntax {
Parenthesized {
open_paren: SpanInfo,
close_paren: SpanInfo,
},
SpaceSeparated,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstCommand {
pub command: String,
pub at_token: SpanInfo,
pub name_span: SpanInfo,
pub arguments: Vec<CstArgument>,
pub syntax: CommandSyntax,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
}
impl CstCommand {
pub fn to_ast(&self) -> format::CommandLine {
format::CommandLine {
command: self.command.clone(),
arguments: self.arguments.iter().map(|a| a.to_ast()).collect(),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstSystemCall {
pub command: String,
pub hash_token: SpanInfo,
pub name_span: SpanInfo,
pub arguments: Vec<CstArgument>,
pub syntax: CommandSyntax,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
}
impl CstSystemCall {
pub fn to_ast(&self) -> format::SystemCallLine {
format::SystemCallLine {
command: self.command.clone(),
arguments: self.arguments.iter().map(|a| a.to_ast()).collect(),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstArgument {
pub name: String,
pub name_span: SpanInfo,
pub equals_token: Option<SpanInfo>,
pub value: Option<CstValue>,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
pub trailing_trivia: Vec<CstTrivia>,
}
impl CstArgument {
pub fn to_ast(&self) -> format::Argument {
format::Argument {
name: self.name.clone(),
value: self
.value
.as_ref()
.map(|v| v.to_ast())
.unwrap_or(format::RValue::Literal(format::Literal::Boolean(true))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum QuoteStyle {
Double, Single, Backtick, }
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstValueKind {
String {
quote: QuoteStyle,
},
TemplateString,
Integer,
Float,
Boolean,
Variable,
Array,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstValue {
pub kind: CstValueKind,
pub raw: String,
pub parsed: format::RValue,
pub span: SpanInfo,
}
impl CstValue {
pub fn to_ast(&self) -> format::RValue {
self.parsed.clone()
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstParagraph {
pub name: String,
pub colon_token: SpanInfo,
pub name_span: SpanInfo,
pub parameters: Vec<CstParameter>,
pub open_paren: Option<SpanInfo>,
pub close_paren: Option<SpanInfo>,
pub block: CstBlock,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
}
impl CstParagraph {
pub fn to_ast(&self) -> crate::error::Result<format::Paragraph> {
Ok(format::Paragraph {
name: self.name.clone(),
parameters: self.parameters.iter().map(|p| p.to_ast()).collect(),
block: self.block.to_ast()?,
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstParameter {
pub name: String,
pub name_span: SpanInfo,
pub equals_token: Option<SpanInfo>,
pub default_value: Option<CstValue>,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
pub trailing_trivia: Vec<CstTrivia>,
}
impl CstParameter {
pub fn to_ast(&self) -> format::Parameter {
format::Parameter {
name: self.name.clone(),
default_value: self.default_value.as_ref().and_then(|v| match &v.parsed {
format::RValue::Literal(lit) => Some(lit.clone()),
_ => None,
}),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstBlock {
pub open_brace: SpanInfo,
pub children: Vec<CstNode>,
pub close_brace: SpanInfo,
pub span: SpanInfo,
}
impl CstBlock {
pub fn to_ast(&self) -> crate::error::Result<format::Block> {
let mut children = Vec::new();
let mut pending_attributes: Vec<format::Attribute> = Vec::new();
for node in &self.children {
match node {
CstNode::Attribute(attr) => {
pending_attributes.push(attr.to_ast());
}
CstNode::Command(cmd) => {
children.push(format::Child {
attributes: std::mem::take(&mut pending_attributes),
content: format::ChildContent::CommandLine(cmd.to_ast()),
});
}
CstNode::SystemCall(sc) => {
children.push(format::Child {
attributes: std::mem::take(&mut pending_attributes),
content: format::ChildContent::SystemCallLine(sc.to_ast()),
});
}
CstNode::TextLine(tl) => {
let mut child = tl.to_ast()?;
child.attributes = std::mem::take(&mut pending_attributes);
children.push(child);
}
CstNode::Block(b) => {
children.push(format::Child {
attributes: std::mem::take(&mut pending_attributes),
content: format::ChildContent::Block(b.to_ast()?),
});
}
CstNode::EmbeddedCode(ec) => {
children.push(format::Child {
attributes: std::mem::take(&mut pending_attributes),
content: format::ChildContent::EmbeddedCode(ec.code.clone()),
});
}
CstNode::Trivia(_) => {
}
CstNode::Paragraph(_) => {
}
CstNode::Error { .. } => {
}
}
}
Ok(format::Block { children })
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstTextLine {
pub leading: Option<CstLeadingText>,
pub text: Option<CstText>,
pub tailing: Option<CstTailingText>,
pub span: SpanInfo,
pub leading_trivia: Vec<CstTrivia>,
}
impl CstTextLine {
pub fn to_ast(&self) -> crate::error::Result<format::Child> {
let leading_ast = match &self.leading {
Some(l) => l.to_ast(),
None => format::LeadingText::None,
};
let text_ast = match &self.text {
Some(t) => t.to_ast()?,
None => format::Text::None,
};
let tailing_ast = match &self.tailing {
Some(t) => t.to_ast(),
None => format::TailingText::None,
};
Ok(format::Child {
attributes: vec![],
content: format::ChildContent::TextLine(leading_ast, text_ast, tailing_ast),
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstLeadingText {
pub open_bracket: SpanInfo,
pub content: CstLeadingTextContent,
pub close_bracket: SpanInfo,
pub span: SpanInfo,
}
impl CstLeadingText {
pub fn to_ast(&self) -> format::LeadingText {
match &self.content {
CstLeadingTextContent::Text(text) => format::LeadingText::Text(text.clone()),
CstLeadingTextContent::Template(tpl) => {
format::LeadingText::TemplateLiteral(tpl.to_ast())
}
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstLeadingTextContent {
Text(String),
Template(CstTemplateLiteral),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstText {
pub kind: CstTextKind,
pub raw: String,
pub parsed: String,
pub span: SpanInfo,
}
impl CstText {
pub fn to_ast(&self) -> crate::error::Result<format::Text> {
Ok(match &self.kind {
CstTextKind::Bare => format::Text::Text(self.parsed.clone()),
CstTextKind::Quoted(_) => format::Text::Text(self.parsed.clone()),
CstTextKind::Template(tpl) => format::Text::TemplateLiteral(tpl.to_ast()),
})
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstTextKind {
Bare,
Quoted(QuoteStyle),
Template(CstTemplateLiteral),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstTailingText {
pub hash_token: SpanInfo,
pub marker: String,
pub marker_span: SpanInfo,
pub span: SpanInfo,
}
impl CstTailingText {
pub fn to_ast(&self) -> format::TailingText {
format::TailingText::Text(self.marker.clone())
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstTemplateLiteral {
pub parts: Vec<CstTemplatePart>,
pub span: SpanInfo,
}
impl CstTemplateLiteral {
pub fn to_ast(&self) -> format::TemplateLiteral {
let parts = self.parts.iter().map(|p| p.to_ast()).collect();
format::TemplateLiteral { parts }
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CstTemplatePart {
Text { content: String, span: SpanInfo },
Value {
open_token: SpanInfo,
variable: format::Variable,
variable_span: SpanInfo,
close_token: SpanInfo,
span: SpanInfo,
},
}
impl CstTemplatePart {
pub fn to_ast(&self) -> format::TemplateLiteralPart {
match self {
CstTemplatePart::Text { content, .. } => {
format::TemplateLiteralPart::Text(content.clone())
}
CstTemplatePart::Value { variable, .. } => {
format::TemplateLiteralPart::Value(format::RValue::Variable(variable.clone()))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EmbeddedCodeSyntax {
Brace, Hash, }
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CstEmbeddedCode {
pub syntax: EmbeddedCodeSyntax,
pub code: String,
pub span: SpanInfo,
}
impl CstEmbeddedCode {
pub fn to_ast(&self) -> crate::error::Result<format::Child> {
Ok(format::Child {
attributes: vec![],
content: format::ChildContent::EmbeddedCode(self.code.clone()),
})
}
}