use wdl_ast::AstNode;
use wdl_ast::AstToken;
use wdl_ast::Diagnostic;
use wdl_ast::Span;
use wdl_ast::lexer::v1::EscapeToken;
use wdl_ast::lexer::v1::Logos;
use wdl_ast::v1;
use wdl_ast::v1::LiteralStringKind;
use wdl_ast::v1::PlaceholderOption;
use crate::Diagnostics;
use crate::VisitReason;
use crate::Visitor;
fn unknown_escape_sequence(sequence: &str, span: Span) -> Diagnostic {
Diagnostic::error(format!("unknown escape sequence `{sequence}`"))
.with_label("this is not a valid WDL escape sequence", span)
}
fn invalid_line_continuation(span: Span) -> Diagnostic {
Diagnostic::error("literal strings may not contain line continuations")
.with_label("remove this line continuation", span)
}
fn invalid_octal_escape(span: Span) -> Diagnostic {
Diagnostic::error("invalid octal escape sequence").with_label(
"expected a sequence of three octal digits to follow this",
span,
)
}
fn invalid_hex_escape(span: Span) -> Diagnostic {
Diagnostic::error("invalid hex escape sequence").with_label(
"expected a sequence of two hexadecimal digits to follow this",
span,
)
}
fn invalid_short_unicode_escape(span: Span) -> Diagnostic {
Diagnostic::error("invalid unicode escape sequence").with_label(
"expected a sequence of four hexadecimal digits to follow this",
span,
)
}
fn invalid_unicode_escape(span: Span) -> Diagnostic {
Diagnostic::error("invalid unicode escape sequence").with_label(
"expected a sequence of eight hexadecimal digits to follow this",
span,
)
}
fn must_escape_newline(span: Span) -> Diagnostic {
Diagnostic::error("literal strings cannot contain newline characters")
.with_label("escape this newline with `\\n`", span)
}
fn must_escape_tab(span: Span) -> Diagnostic {
Diagnostic::error("literal strings cannot contain tab characters")
.with_label("escape this tab with `\\t`", span)
}
fn multiple_placeholder_options(first: Span, additional: Span) -> Diagnostic {
Diagnostic::error("a placeholder cannot have more than one option")
.with_label("duplicate placeholder option is here", additional)
.with_label("first placeholder option is here", first)
}
fn check_text(diagnostics: &mut Diagnostics, start: usize, text: &str) {
let lexer = EscapeToken::lexer(text).spanned();
for (token, span) in lexer {
match token.expect("should lex") {
EscapeToken::Valid
| EscapeToken::ValidOctal
| EscapeToken::ValidHex
| EscapeToken::ValidUnicode
| EscapeToken::Text => continue,
EscapeToken::InvalidOctal => {
diagnostics.add(invalid_octal_escape(Span::new(start + span.start, 1)))
}
EscapeToken::InvalidHex => diagnostics.add(invalid_hex_escape(Span::new(
start + span.start,
span.len(),
))),
EscapeToken::InvalidShortUnicode => diagnostics.add(invalid_short_unicode_escape(
Span::new(start + span.start, span.len()),
)),
EscapeToken::InvalidUnicode => diagnostics.add(invalid_unicode_escape(Span::new(
start + span.start,
span.len(),
))),
EscapeToken::Continuation => diagnostics.add(invalid_line_continuation(Span::new(
start + span.start,
span.len(),
))),
EscapeToken::Newline => diagnostics.add(must_escape_newline(Span::new(
start + span.start,
span.len(),
))),
EscapeToken::Tab => {
diagnostics.add(must_escape_tab(Span::new(start + span.start, span.len())))
}
EscapeToken::Unknown => diagnostics.add(unknown_escape_sequence(
&text[span.start..span.end],
Span::new(start + span.start, span.len()),
)),
}
}
}
#[derive(Default, Debug)]
pub struct LiteralTextVisitor;
impl Visitor for LiteralTextVisitor {
fn reset(&mut self) {
*self = Default::default();
}
fn string_text(&mut self, diagnostics: &mut Diagnostics, text: &v1::StringText) {
let string: v1::LiteralString<_> = text.parent().expect("should have a parent");
match string.kind() {
LiteralStringKind::SingleQuoted | LiteralStringKind::DoubleQuoted => {
check_text(diagnostics, text.span().start(), text.text());
}
LiteralStringKind::Multiline => {
}
}
}
fn placeholder(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
placeholder: &v1::Placeholder,
) {
if reason == VisitReason::Exit {
return;
}
let mut placeholders = placeholder.children::<PlaceholderOption<_>>();
if let Some(first) = placeholders.next() {
for additional in placeholders {
diagnostics.add(multiple_placeholder_options(
first.span(),
additional.span(),
));
}
}
}
}