#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub line: usize,
pub column: usize,
pub offset: usize,
pub length: usize,
}
impl Span {
pub fn new(line: usize, column: usize, offset: usize, length: usize) -> Self {
Self {
line,
column,
offset,
length,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
Integer(i64),
Float(f64),
String(StringInfo),
Bool(bool),
Null,
Identifier(String),
ClassName,
Extends,
Class,
Func,
Var,
Const,
Signal,
Enum,
Static,
If,
Elif,
Else,
For,
While,
Match,
When,
Break,
Continue,
Pass,
Return,
As,
Is,
In,
Not,
And,
Or,
Self_,
Super,
Await,
Assert,
Breakpoint,
Preload,
Void,
Trait,
Annotation(String),
Plus,
Minus,
Star,
StarStar,
Slash,
Percent,
Equal,
NotEqual,
Less,
LessEqual,
Greater,
GreaterEqual,
Assign,
PlusAssign,
MinusAssign,
StarAssign,
SlashAssign,
PercentAssign,
StarStarAssign,
LessLessAssign,
GreaterGreaterAssign,
AmpersandAssign,
PipeAssign,
CaretAssign,
Ampersand,
Pipe,
Caret,
Tilde,
LessLess,
GreaterGreater,
Arrow,
AmpersandAmpersand,
PipePipe,
Bang,
LeftParen,
RightParen,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
Comma,
Colon,
Semicolon,
Dot,
DotDot,
Ellipsis,
Dollar,
UniqueNodeMarker,
Newline,
Indent,
Dedent,
Comment(String),
DocComment(String),
Eof,
Error(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct StringInfo {
pub value: String,
pub quote_style: QuoteStyle,
pub prefix: StringPrefix,
pub is_multiline: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuoteStyle {
Single,
Double,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StringPrefix {
None,
Raw,
StringName,
NodePath,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
pub text: String,
}
impl Token {
pub fn new(kind: TokenKind, span: Span, text: String) -> Self {
Self { kind, span, text }
}
}
pub struct StringLines {
interior: Vec<bool>,
ends_inside: Vec<bool>,
literal_end: Vec<Option<usize>>,
}
impl StringLines {
pub fn new(tokens: &[Token], line_count: usize) -> Self {
let mut interior = vec![false; line_count];
let mut ends_inside = vec![false; line_count];
let mut literal_end = vec![None; line_count];
for token in tokens {
if !matches!(token.kind, TokenKind::String(_)) {
continue;
}
let first = token.span.line;
let last = first + token.text.matches('\n').count();
if last > first {
if let Some(slot) = last.checked_sub(1).and_then(|idx| literal_end.get_mut(idx)) {
*slot = token.text.rsplit('\n').next().map(str::len);
}
}
for line in first..=last {
let Some(idx) = line.checked_sub(1).filter(|&idx| idx < line_count) else {
break;
};
interior[idx] |= line > first;
ends_inside[idx] |= line < last;
}
}
Self {
interior,
ends_inside,
literal_end,
}
}
pub fn is_interior(&self, idx: usize) -> bool {
self.interior.get(idx).copied().unwrap_or(false)
}
pub fn ends_inside(&self, idx: usize) -> bool {
self.ends_inside.get(idx).copied().unwrap_or(false)
}
pub fn literal_end(&self, idx: usize) -> Option<usize> {
self.literal_end.get(idx).copied().flatten()
}
}