use crate::parse::context::ParseContext;
use crate::parse::engine::{Engine, ParseError};
use crate::parse::insn::ParseGraph;
use crate::types::{Pos, Span, SyntaxKind, TreeEvent};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EmbeddedSpan {
TokenKind(SyntaxKind),
NodeKind(SyntaxKind),
}
#[derive(Clone, Copy, Debug)]
pub struct SubLanguage<'a> {
pub host_kind: SyntaxKind,
pub span: EmbeddedSpan,
pub embedded: ParseGraph<'a>,
pub wrapper_kind: Option<SyntaxKind>,
pub error_kind: Option<SyntaxKind>,
}
#[derive(Clone, Debug)]
pub struct SubLanguageError {
pub host_span: Span,
pub error: ParseError,
}
pub fn apply_sublanguages(
engine: &mut Engine,
input: &[u8],
host_events: &[TreeEvent],
sublanguages: &[SubLanguage<'_>],
) -> (Vec<TreeEvent>, Vec<SubLanguageError>) {
let mut out: Vec<TreeEvent> = Vec::with_capacity(host_events.len());
let mut errors: Vec<SubLanguageError> = Vec::new();
#[derive(Clone, Debug)]
struct NodeFrame {
kind: SyntaxKind,
open_pos: Pos,
host_idx: Option<usize>,
embed_span: Option<Span>,
}
let mut stack: Vec<NodeFrame> = Vec::new();
for &ev in host_events {
match ev {
TreeEvent::NodeOpen { kind, field, pos } => {
let host_idx = sublanguages.iter().position(|s| s.host_kind == kind);
stack.push(NodeFrame {
kind,
open_pos: pos,
host_idx,
embed_span: None,
});
out.push(TreeEvent::NodeOpen { kind, field, pos });
}
TreeEvent::Token {
kind,
start,
end,
is_trivia,
} => {
if let Some(top) = stack.last_mut() {
if let Some(i) = top.host_idx {
if matches!(sublanguages[i].span, EmbeddedSpan::TokenKind(k) if k == kind) {
top.embed_span = Some(Span::new(start, end));
}
}
}
out.push(TreeEvent::Token {
kind,
start,
end,
is_trivia,
});
}
TreeEvent::NodeClose { pos } => {
let frame = match stack.pop() {
Some(f) => f,
None => {
out.push(TreeEvent::NodeClose { pos });
continue;
}
};
if let Some(parent) = stack.last_mut() {
if let Some(i) = parent.host_idx {
if matches!(sublanguages[i].span, EmbeddedSpan::NodeKind(k) if k == frame.kind)
{
parent.embed_span = Some(Span::new(frame.open_pos, pos));
}
}
}
if let Some(i) = frame.host_idx {
if let Some(span) = frame.embed_span {
let slice = span.as_slice(input);
let embedded_ctx = sublanguages[i]
.error_kind
.map(|k| ParseContext::new().with_error_node_kind(k))
.unwrap_or_default();
let embedded_out = match engine.parse_with_context(
&sublanguages[i].embedded,
slice,
&embedded_ctx,
) {
Ok(o) => Some(o),
Err(e) => {
if sublanguages[i].error_kind.is_some() {
match engine.parse_recovering_with_context(
&sublanguages[i].embedded,
slice,
&embedded_ctx,
) {
Ok(o) => Some(o),
Err((partial, e2)) => {
errors.push(SubLanguageError {
host_span: span,
error: e2,
});
Some(partial)
}
}
} else {
errors.push(SubLanguageError {
host_span: span,
error: e,
});
None
}
}
};
if let Some(embedded_out) = embedded_out {
if let Some(wk) = sublanguages[i].wrapper_kind {
out.push(TreeEvent::NodeOpen {
kind: wk,
field: None,
pos: span.start,
});
}
out.extend(
embedded_out
.tree_events
.into_iter()
.map(|e| offset_event(e, span.start)),
);
if sublanguages[i].wrapper_kind.is_some() {
out.push(TreeEvent::NodeClose { pos: span.end });
}
}
}
}
out.push(TreeEvent::NodeClose { pos });
}
}
}
(out, errors)
}
fn offset_event(ev: TreeEvent, base: Pos) -> TreeEvent {
match ev {
TreeEvent::NodeOpen { kind, field, pos } => TreeEvent::NodeOpen {
kind,
field,
pos: pos.saturating_add(base),
},
TreeEvent::NodeClose { pos } => TreeEvent::NodeClose {
pos: pos.saturating_add(base),
},
TreeEvent::Token {
kind,
start,
end,
is_trivia,
} => TreeEvent::Token {
kind,
start: start.saturating_add(base),
end: end.saturating_add(base),
is_trivia,
},
}
}