use core::num;
use crate::{
ast::AstToken,
SyntaxKind::{self, *},
SyntaxToken,
};
macro_rules! token {
(
#[from($kind:ident)]
$(#[$meta:meta])*
struct $name:ident;
) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
$(#[$meta])*
pub struct $name(pub(super) SyntaxToken);
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl AstToken for $name {
fn can_cast(kind: SyntaxKind) -> bool {
$kind == kind
}
fn cast(from: SyntaxToken) -> Option<Self> {
if from.kind() == $kind {
Some(Self(from))
} else {
None
}
}
fn syntax(&self) -> &SyntaxToken {
&self.0
}
}
};
}
token! { #[from(TOKEN_WHITESPACE)] struct Whitespace; }
token! { #[from(TOKEN_COMMENT)] struct Comment; }
impl Comment {
pub fn text(&self) -> &str {
let text = self.syntax().text();
match text.strip_prefix('#') {
Some(s) => s,
None => text.strip_prefix(r#"/*"#).unwrap().strip_suffix(r#"*/"#).unwrap(),
}
}
}
#[cfg(test)]
mod tests {
use crate::ast;
use crate::match_ast;
use crate::Root;
use super::*;
use rowan::ast::AstNode;
#[test]
fn comment() {
let s = "# comment bruh
/* this is a multiline comment wow
asdfasdf
asdfasdf */
1 + 1
/* last one */
";
let comments: Vec<String> = Root::parse(s)
.ok()
.unwrap()
.syntax()
.children_with_tokens()
.filter_map(|e| match e {
rowan::NodeOrToken::Token(token) => match_ast! { match token {
ast::Comment(it) => Some(it.text().to_string()),
_ => None,
}},
rowan::NodeOrToken::Node(_) => None,
})
.collect();
let expected = vec![
" comment bruh",
" this is a multiline comment wow\nasdfasdf\nasdfasdf ",
" last one ",
];
assert_eq!(comments, expected);
}
}
token! { #[from(TOKEN_FLOAT)] struct Float; }
impl Float {
pub fn value(&self) -> Result<f64, num::ParseFloatError> {
self.syntax().text().parse()
}
}
token! { #[from(TOKEN_INTEGER)] struct Integer; }
impl Integer {
pub fn value(&self) -> Result<i64, num::ParseIntError> {
self.syntax().text().parse()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PathContent(pub(super) SyntaxToken);
impl PathContent {
pub fn text(&self) -> &str {
self.syntax().text()
}
pub fn path_kind(&self) -> SyntaxKind {
self.syntax().kind()
}
pub fn is_absolute(&self) -> bool {
self.path_kind() == TOKEN_PATH_ABS
}
pub fn is_relative(&self) -> bool {
self.path_kind() == TOKEN_PATH_REL
}
pub fn is_home(&self) -> bool {
self.path_kind() == TOKEN_PATH_HOME
}
pub fn is_search(&self) -> bool {
self.path_kind() == TOKEN_PATH_SEARCH
}
}
impl AstToken for PathContent {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, TOKEN_PATH_ABS | TOKEN_PATH_REL | TOKEN_PATH_HOME | TOKEN_PATH_SEARCH)
}
fn cast(from: SyntaxToken) -> Option<Self> {
if Self::can_cast(from.kind()) {
Some(Self(from))
} else {
None
}
}
fn syntax(&self) -> &SyntaxToken {
&self.0
}
}
token! { #[from(TOKEN_STRING_CONTENT)] struct StrContent; }
token! { #[from(TOKEN_URI)] struct Uri; }