use alloc::vec::Vec;
use syntax_lang::{Builder, Element, Node, Token};
use crate::kind::Kind;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct Event {
a: u32,
b: u32,
}
const TAG: u32 = 0xFFFF_FFF0;
impl Event {
pub(crate) const TOMBSTONE: Self = Self { a: 0, b: TAG };
pub(crate) const TOKEN: Self = Self { a: 0, b: TAG + 1 };
pub(crate) const UNLABEL: Self = Self { a: 0, b: TAG + 2 };
pub(crate) const FINISH: Self = Self { a: 0, b: TAG + 3 };
#[inline]
pub(crate) const fn start(kind: Kind, forward: u32) -> Self {
Self {
a: kind.bits(),
b: forward,
}
}
#[inline]
pub(crate) const fn token_as(kind: Kind) -> Self {
Self {
a: kind.bits(),
b: TAG + 4,
}
}
#[inline]
pub(crate) const fn label(label: u16) -> Self {
Self {
a: label as u32,
b: TAG + 5,
}
}
#[inline]
pub(crate) const fn step(self) -> Step {
match self.b {
b if b < TAG => Step::Start {
kind: Kind::from_bits(self.a),
forward: b,
},
b if b == TAG + 1 => Step::Token,
b if b == TAG + 2 => Step::Unlabel,
b if b == TAG + 3 => Step::Finish,
b if b == TAG + 4 => Step::TokenAs(Kind::from_bits(self.a)),
b if b == TAG + 5 => Step::Label(self.a as u16),
_ => Step::Tombstone,
}
}
}
impl core::fmt::Debug for Event {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.step().fmt(f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Step {
Start { kind: Kind, forward: u32 },
Tombstone,
Token,
TokenAs(Kind),
Label(u16),
Unlabel,
Finish,
}
pub(crate) fn build(
tokens: &[Token<Kind>],
events: &mut [Event],
fallback: Kind,
error: Kind,
) -> Node<Kind> {
let mut builder = Builder::new();
let mut next = 0;
let mut open = 0usize;
let mut chain: Vec<Kind> = Vec::new();
let mut scopes: Vec<(usize, u16)> = Vec::new();
let label_at = |scopes: &[(usize, u16)], open: usize| match scopes.last() {
Some(&(depth, label)) if depth == open => Some(label),
_ => None,
};
for i in 0..events.len() {
match events[i].step() {
Step::Start { kind, forward } => {
chain.clear();
chain.push(kind);
let mut link = forward as usize;
while link != 0 {
let Step::Start { kind, forward } = events[link].step() else {
break;
};
chain.push(kind);
events[link] = Event::TOMBSTONE;
link = forward as usize;
}
if open > 0 {
next = trivia(&mut builder, tokens, next);
}
for &kind in chain.iter().rev() {
let kind = if kind.label().is_some() || kind == error {
kind
} else {
kind.with_label(label_at(&scopes, open))
};
builder.start_node(kind);
open += 1;
}
}
Step::Token => {
next = trivia(&mut builder, tokens, next);
if let Some(&token) = tokens.get(next) {
let label = label_at(&scopes, open);
builder.token(match label {
Some(_) => Token::new(token.kind().with_label(label), token.span()),
None => token,
});
next += 1;
}
}
Step::TokenAs(kind) => {
next = trivia(&mut builder, tokens, next);
if let Some(&token) = tokens.get(next) {
let kind = kind.with_label(label_at(&scopes, open));
builder.token(Token::new(kind, token.span()));
next += 1;
}
}
Step::Label(label) => scopes.push((open, label)),
Step::Unlabel => {
let _ = scopes.pop();
}
Step::Finish => {
if open == 1 {
for &token in &tokens[next.min(tokens.len())..] {
builder.token(token);
}
next = tokens.len();
}
open = open.saturating_sub(1);
builder.finish_node();
}
Step::Tombstone => {}
}
}
match builder.finish() {
Ok(root) if next == tokens.len() => root,
_ => {
debug_assert!(false, "unbalanced parse events");
Node::new(
fallback,
tokens.iter().map(|t| Element::Token(*t)).collect(),
)
}
}
}
#[inline]
fn trivia(builder: &mut Builder<Kind>, tokens: &[Token<Kind>], mut next: usize) -> usize {
while let Some(&token) = tokens.get(next) {
if !token.is_trivia() {
break;
}
builder.token(token);
next += 1;
}
next
}
#[cfg(test)]
mod tests {
use alloc::vec;
use syntax_lang::Span;
use super::*;
const ROOT: Kind = Kind::new(20, false);
const EXPR: Kind = Kind::new(21, false);
const BIN: Kind = Kind::new(22, false);
const NUM: Kind = Kind::new(5, false);
const PLUS: Kind = Kind::new(7, false);
const WS: Kind = Kind::new(1, true);
fn tok(kind: Kind, start: u32, end: u32) -> Token<Kind> {
Token::new(kind, Span::new(start, end))
}
fn tokens() -> Vec<Token<Kind>> {
vec![
tok(WS, 0, 1),
tok(NUM, 1, 2),
tok(WS, 2, 3),
tok(PLUS, 3, 4),
tok(WS, 4, 5),
tok(NUM, 5, 6),
tok(PLUS, 6, 7),
tok(NUM, 7, 8),
tok(WS, 8, 9),
]
}
fn outline(node: &Node<Kind>, out: &mut Vec<(usize, u32, u32)>) {
out.push((
node.kind().slot(),
node.span().start().to_u32(),
node.span().end().to_u32(),
));
for child in node.child_nodes() {
outline(child, out);
}
}
#[test]
fn test_build_forward_links_wrap_left_operands() {
let mut events = vec![
Event::start(ROOT, 0),
Event::start(EXPR, 0),
Event::start(BIN, 7),
Event::TOKEN,
Event::TOKEN,
Event::TOKEN,
Event::FINISH,
Event::start(BIN, 0),
Event::TOKEN,
Event::TOKEN,
Event::FINISH,
Event::FINISH,
Event::FINISH,
];
let root = build(&tokens(), &mut events, ROOT, ROOT);
let mut out = Vec::new();
outline(&root, &mut out);
assert_eq!(out, [(20, 0, 9), (21, 1, 8), (22, 1, 8), (22, 1, 6)]);
assert_eq!(root.tokens().count(), 9);
}
#[test]
fn test_build_places_trivia_outside_nodes() {
let mut events = vec![
Event::start(ROOT, 0),
Event::TOMBSTONE,
Event::start(EXPR, 0),
Event::TOKEN,
Event::TOKEN,
Event::TOKEN,
Event::TOKEN,
Event::TOKEN,
Event::FINISH,
Event::FINISH,
];
let root = build(&tokens(), &mut events, ROOT, ROOT);
let mut out = Vec::new();
outline(&root, &mut out);
assert_eq!(out, [(20, 0, 9), (21, 1, 8)]);
}
#[test]
fn test_build_unbalanced_events_fall_back_losslessly() {
let mut events = vec![Event::start(ROOT, 0), Event::TOKEN];
let result = std::panic::catch_unwind(move || build(&tokens(), &mut events, ROOT, ROOT));
if let Ok(root) = result {
assert_eq!(root.tokens().count(), 9);
}
}
}