use std::{fmt, slice::Iter};
use rowan::{GreenNode, GreenNodeBuilder};
use crate::{ast::Document, Error, SyntaxElement, SyntaxKind, SyntaxNode};
use super::LimitTracker;
#[derive(PartialEq, Eq, Clone)]
pub struct SyntaxTree {
pub(crate) green: GreenNode,
pub(crate) errors: Vec<crate::Error>,
pub(crate) recursion_limit: LimitTracker,
pub(crate) token_limit: LimitTracker,
}
impl SyntaxTree {
pub fn errors(&self) -> Iter<'_, crate::Error> {
self.errors.iter()
}
pub fn recursion_limit(&self) -> LimitTracker {
self.recursion_limit
}
pub fn token_limit(&self) -> LimitTracker {
self.token_limit
}
pub fn green(&self) -> GreenNode {
self.green.clone()
}
pub(crate) fn syntax_node(&self) -> SyntaxNode {
rowan::SyntaxNode::new_root(self.green.clone())
}
pub fn document(self) -> Document {
Document {
syntax: self.syntax_node(),
}
}
}
impl fmt::Debug for SyntaxTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn print(f: &mut fmt::Formatter<'_>, indent: usize, element: SyntaxElement) -> fmt::Result {
let kind: SyntaxKind = element.kind();
write!(f, "{:indent$}", "", indent = indent)?;
match element {
rowan::NodeOrToken::Node(node) => {
writeln!(f, "- {:?}@{:?}", kind, node.text_range())?;
for child in node.children_with_tokens() {
print(f, indent + 4, child)?;
}
Ok(())
}
rowan::NodeOrToken::Token(token) => {
writeln!(
f,
"- {:?}@{:?} {:?}",
kind,
token.text_range(),
token.text()
)
}
}
}
fn print_err(f: &mut fmt::Formatter<'_>, errors: Vec<Error>) -> fmt::Result {
for err in errors {
writeln!(f, "- {err:?}")?;
}
write!(f, "")
}
fn print_recursion_limit(
f: &mut fmt::Formatter<'_>,
recursion_limit: LimitTracker,
) -> fmt::Result {
write!(f, "{recursion_limit:?}")
}
print(f, 0, self.syntax_node().into())?;
print_err(f, self.errors.clone())?;
print_recursion_limit(f, self.recursion_limit)
}
}
#[derive(Debug)]
pub(crate) struct SyntaxTreeBuilder {
builder: GreenNodeBuilder<'static>,
}
impl SyntaxTreeBuilder {
pub(crate) fn new() -> Self {
Self {
builder: GreenNodeBuilder::new(),
}
}
pub(crate) fn checkpoint(&self) -> rowan::Checkpoint {
self.builder.checkpoint()
}
pub(crate) fn start_node(&mut self, kind: SyntaxKind) {
self.builder.start_node(rowan::SyntaxKind(kind as u16));
}
pub(crate) fn finish_node(&mut self) {
self.builder.finish_node();
}
pub(crate) fn wrap_node(&mut self, checkpoint: rowan::Checkpoint, kind: SyntaxKind) {
self.builder
.start_node_at(checkpoint, rowan::SyntaxKind(kind as u16));
}
pub(crate) fn token(&mut self, kind: SyntaxKind, text: &str) {
self.builder.token(rowan::SyntaxKind(kind as u16), text);
}
pub(crate) fn finish(
self,
errors: Vec<Error>,
recursion_limit: LimitTracker,
token_limit: LimitTracker,
) -> SyntaxTree {
SyntaxTree {
green: self.builder.finish(),
errors,
recursion_limit,
token_limit,
}
}
}
#[cfg(test)]
mod test {
use crate::ast::Definition;
use crate::Parser;
#[test]
fn directive_name() {
let input = "directive @example(isTreat: Boolean, treatKind: String) on FIELD | MUTATION";
let parser = Parser::new(input);
let ast = parser.parse();
let doc = ast.document();
for def in doc.definitions() {
if let Definition::DirectiveDefinition(directive) = def {
assert_eq!(directive.name().unwrap().text(), "example");
}
}
}
#[test]
fn object_type_definition() {
let input = "
type ProductDimension {
size: String
weight: Float @tag(name: \"hi from inventory value type field\")
}
";
let parser = Parser::new(input);
let ast = parser.parse();
assert_eq!(0, ast.errors().len());
let doc = ast.document();
for def in doc.definitions() {
if let Definition::ObjectTypeDefinition(object_type) = def {
assert_eq!(object_type.name().unwrap().text(), "ProductDimension");
for field_def in object_type.fields_definition().unwrap().field_definitions() {
println!("{}", field_def.name().unwrap().text()); }
}
}
}
}