forked from katzen-cafe/iowo
192 lines
5.9 KiB
Rust
192 lines
5.9 KiB
Rust
use owo_colors::{unset_override, OwoColorize};
|
|
use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken};
|
|
use std::mem;
|
|
|
|
use crate::lst_parser::{
|
|
input::MEANINGLESS_TOKS,
|
|
syntax_kind::{Lang, SyntaxKind},
|
|
};
|
|
|
|
use super::{error::SyntaxError, events::Event};
|
|
|
|
pub struct Output {
|
|
pub green_node: GreenNode,
|
|
pub errors: Vec<SyntaxError>,
|
|
}
|
|
|
|
impl std::fmt::Debug for Output {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let mut errs: Vec<&SyntaxError> = self.errors.iter().collect();
|
|
errs.reverse();
|
|
|
|
debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0, &mut errs, false)
|
|
}
|
|
}
|
|
|
|
fn debug_print_green_node(
|
|
node: NodeOrToken<&GreenNodeData, &GreenTokenData>,
|
|
f: &mut dyn std::fmt::Write,
|
|
lvl: i32,
|
|
errs: &mut Vec<&SyntaxError>,
|
|
colored: bool,
|
|
) -> std::fmt::Result {
|
|
for _ in 0..lvl {
|
|
f.write_str(" ")?;
|
|
}
|
|
|
|
if !colored {
|
|
owo_colors::set_override(false);
|
|
} else {
|
|
owo_colors::set_override(true);
|
|
}
|
|
|
|
let r = match node {
|
|
NodeOrToken::Node(n) => {
|
|
let kind = Lang::kind_from_raw(node.kind());
|
|
if kind != SyntaxKind::PARSE_ERR {
|
|
writeln!(
|
|
f,
|
|
"{:?} {}",
|
|
Lang::kind_from_raw(node.kind()).bright_yellow().bold(),
|
|
"{".yellow()
|
|
)?;
|
|
} else {
|
|
let err = errs
|
|
.pop()
|
|
.expect("all error syntax nodes should correspond to an error")
|
|
.bright_red();
|
|
|
|
writeln!(
|
|
f,
|
|
"{:?}{} {err:?} {}",
|
|
kind.bright_red().bold(),
|
|
":".red(),
|
|
"{".bright_red().bold()
|
|
)?;
|
|
}
|
|
for c in n.children() {
|
|
debug_print_green_node(c, f, lvl + 1, errs, colored)?;
|
|
}
|
|
for _ in 0..lvl {
|
|
f.write_str(" ")?;
|
|
}
|
|
if kind != SyntaxKind::PARSE_ERR {
|
|
write!(f, "{}", "}\n".yellow())
|
|
} else {
|
|
write!(f, "{}", "}\n".bright_red().bold())
|
|
}
|
|
}
|
|
NodeOrToken::Token(t) => {
|
|
let tok = Lang::kind_from_raw(t.kind());
|
|
if MEANINGLESS_TOKS.contains(tok) {
|
|
writeln!(
|
|
f,
|
|
"{:?} {:?}{}",
|
|
Lang::kind_from_raw(t.kind()).white(),
|
|
t.text().white(),
|
|
";".white()
|
|
)
|
|
} else {
|
|
writeln!(
|
|
f,
|
|
"{:?} {:?}{}",
|
|
Lang::kind_from_raw(t.kind()).bright_cyan().bold(),
|
|
t.text().green(),
|
|
";".yellow()
|
|
)
|
|
}
|
|
}
|
|
};
|
|
|
|
if !colored {
|
|
owo_colors::unset_override();
|
|
}
|
|
|
|
r
|
|
}
|
|
|
|
impl Output {
|
|
pub fn debug_colored(&self) -> String {
|
|
let mut out = String::new();
|
|
let mut errs: Vec<&SyntaxError> = self.errors.iter().collect();
|
|
errs.reverse();
|
|
|
|
let _ = debug_print_green_node(
|
|
NodeOrToken::Node(&self.green_node),
|
|
&mut out,
|
|
0,
|
|
&mut errs,
|
|
true,
|
|
);
|
|
|
|
out
|
|
}
|
|
pub fn from_parser_output(
|
|
mut raw_toks: Vec<(SyntaxKind, &str)>,
|
|
(mut events, errs): (Vec<Event>, Vec<SyntaxError>),
|
|
) -> Self {
|
|
let mut builder = GreenNodeBuilder::new();
|
|
let mut fw_parents = Vec::new();
|
|
raw_toks.reverse();
|
|
|
|
for i in 0..events.len() {
|
|
match mem::replace(&mut events[i], Event::tombstone()) {
|
|
Event::Start {
|
|
kind,
|
|
forward_parent,
|
|
} => {
|
|
if kind == SyntaxKind::TOMBSTONE && forward_parent.is_none() {
|
|
continue;
|
|
}
|
|
|
|
fw_parents.push(kind);
|
|
let mut idx = i;
|
|
let mut fp = forward_parent;
|
|
while let Some(fwd) = fp {
|
|
idx += fwd as usize;
|
|
fp = match mem::replace(&mut events[idx], Event::tombstone()) {
|
|
Event::Start {
|
|
kind,
|
|
forward_parent,
|
|
} => {
|
|
fw_parents.push(kind);
|
|
forward_parent
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
// remove whitespace bc it's ugly
|
|
while let Some((SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE, _)) =
|
|
raw_toks.last()
|
|
{
|
|
match events.iter_mut().find(|ev| matches!(ev, Event::Eat { .. })) {
|
|
Some(Event::Eat { count }) => *count -= 1,
|
|
_ => unreachable!(),
|
|
}
|
|
|
|
let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap();
|
|
builder.token(tok.into(), text);
|
|
}
|
|
|
|
for kind in fw_parents.drain(..).rev() {
|
|
if kind != SyntaxKind::TOMBSTONE {
|
|
builder.start_node(kind.into());
|
|
}
|
|
}
|
|
}
|
|
Event::Finish => builder.finish_node(),
|
|
Event::Eat { count } => (0..count).for_each(|_| {
|
|
let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap();
|
|
builder.token(tok.into(), text);
|
|
}),
|
|
Event::Error => todo!(),
|
|
}
|
|
}
|
|
|
|
Self {
|
|
green_node: builder.finish(),
|
|
errors: errs,
|
|
}
|
|
}
|
|
}
|