lang: improve and simplify error handling and storage

fixes wrong error ordering with errors using `forward_parents`.
This commit is contained in:
Schrottkatze 2024-06-03 10:53:59 +02:00
parent ed151c2e3c
commit f6da90a354
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
8 changed files with 110 additions and 42 deletions

View file

@ -1,6 +1,12 @@
use drop_bomb::DropBomb;
use self::{error::SyntaxError, events::Event, input::Input, syntax_kind::SyntaxKind};
use self::{
error::SyntaxError,
events::{Event, NodeKind},
input::Input,
syntax_kind::SyntaxKind,
};
use std::cell::Cell;
pub mod syntax_kind;
#[cfg(test)]
@ -12,11 +18,13 @@ pub mod grammar;
pub mod input;
pub mod output;
const PARSER_STEP_LIMIT: u32 = 4096;
pub struct Parser<'src, 'toks> {
input: Input<'src, 'toks>,
pos: usize,
events: Vec<Event>,
errors: Vec<SyntaxError>,
steps: Cell<u32>,
}
impl<'src, 'toks> Parser<'src, 'toks> {
@ -25,15 +33,16 @@ impl<'src, 'toks> Parser<'src, 'toks> {
input,
pos: 0,
events: Vec::new(),
errors: Vec::new(),
steps: Cell::new(0),
}
}
pub fn finish(self) -> (Vec<Event>, Vec<SyntaxError>) {
(self.events, self.errors)
pub fn finish(self) -> Vec<Event> {
self.events
}
pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
self.step();
self.input.kind(self.pos + n)
}
@ -44,6 +53,7 @@ impl<'src, 'toks> Parser<'src, 'toks> {
}
pub(crate) fn current(&self) -> SyntaxKind {
self.step();
self.input.kind(self.pos)
}
@ -80,6 +90,12 @@ impl<'src, 'toks> Parser<'src, 'toks> {
fn push_ev(&mut self, event: Event) {
self.events.push(event)
}
fn step(&self) {
let steps = self.steps.get();
assert!(steps <= PARSER_STEP_LIMIT, "the parser seems stuck...");
self.steps.set(steps + 1);
}
}
pub(crate) struct Marker {
@ -95,12 +111,13 @@ impl Marker {
}
}
pub(crate) fn complete(mut self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker {
fn complete_node(mut self, p: &mut Parser, kind: NodeKind) -> CompletedMarker {
self.bomb.defuse();
match &mut p.events[self.pos] {
Event::Start { kind: slot, .. } => *slot = kind,
Event::Start { kind: slot, .. } => *slot = kind.clone(),
_ => unreachable!(),
}
p.push_ev(Event::Finish);
CompletedMarker {
@ -109,9 +126,12 @@ impl Marker {
}
}
pub(crate) fn complete_err(mut self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker {
p.errors.push(kind);
self.complete(p, SyntaxKind::PARSE_ERR)
pub(crate) fn complete(self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker {
self.complete_node(p, NodeKind::Syntax(kind))
}
pub(crate) fn error(self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker {
self.complete_node(p, NodeKind::Error(kind))
}
pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) {
@ -119,7 +139,7 @@ impl Marker {
if self.pos == p.events.len() - 1 {
match p.events.pop() {
Some(Event::Start {
kind: SyntaxKind::TOMBSTONE,
kind: NodeKind::Syntax(SyntaxKind::TOMBSTONE),
forward_parent: None,
}) => (),
_ => unreachable!(),
@ -130,7 +150,7 @@ impl Marker {
pub(crate) struct CompletedMarker {
pos: usize,
kind: SyntaxKind,
kind: NodeKind,
}
impl CompletedMarker {

View file

@ -1,6 +1,6 @@
use crate::lst_parser::syntax_kind::SyntaxKind;
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SyntaxError {
Expected(Vec<SyntaxKind>),
PipelineNeedsSink,

View file

@ -1,22 +1,69 @@
use crate::lst_parser::syntax_kind::SyntaxKind;
use super::error::SyntaxError;
#[derive(Debug)]
pub enum Event {
Start {
kind: SyntaxKind,
kind: NodeKind,
forward_parent: Option<usize>,
},
Finish,
Eat {
count: usize,
},
Error,
}
#[derive(Debug, Clone, PartialEq)]
pub enum NodeKind {
Syntax(SyntaxKind),
Error(SyntaxError),
}
impl NodeKind {
pub fn is_syntax(&self) -> bool {
matches!(self, Self::Syntax(_))
}
pub fn is_error(&self) -> bool {
matches!(self, Self::Error(_))
}
}
impl From<SyntaxKind> for NodeKind {
fn from(value: SyntaxKind) -> Self {
NodeKind::Syntax(value)
}
}
impl From<SyntaxError> for NodeKind {
fn from(value: SyntaxError) -> Self {
NodeKind::Error(value)
}
}
impl PartialEq<SyntaxKind> for NodeKind {
fn eq(&self, other: &SyntaxKind) -> bool {
match self {
NodeKind::Syntax(s) => s == other,
NodeKind::Error(_) => false,
}
}
}
impl PartialEq<SyntaxError> for NodeKind {
fn eq(&self, other: &SyntaxError) -> bool {
match self {
NodeKind::Syntax(_) => false,
NodeKind::Error(e) => e == other,
}
}
}
impl Event {
pub(crate) fn tombstone() -> Self {
Self::Start {
kind: SyntaxKind::TOMBSTONE,
kind: SyntaxKind::TOMBSTONE.into(),
forward_parent: None,
}
}

View file

@ -35,7 +35,7 @@ pub fn parenthesized_expr(p: &mut Parser) -> Option<CompletedMarker> {
let par_expr = p.start("parenthesized");
expression(p, false);
if !p.eat(R_PAREN) {
return Some(par_expr.complete_err(p, SyntaxError::Expected(vec![R_PAREN])));
return Some(par_expr.error(p, SyntaxError::Expected(vec![R_PAREN])));
}
return Some(par_expr.complete(p, PARENTHESIZED_EXPR));

View file

@ -21,7 +21,7 @@ pub fn vec_matrix_list(p: &mut Parser) -> CompletedMarker {
start.complete(p, LIST)
} else {
row_start.abandon(p);
start.complete_err(p, SyntaxError::Expected(vec![EXPR, R_BRACK]))
start.error(p, SyntaxError::Expected(vec![EXPR, R_BRACK]))
}
}

View file

@ -16,7 +16,7 @@ pub fn pipeline(p: &mut Parser, start_expr: CompletedMarker) -> Option<Completed
loop {
if expression(p, true).is_none() {
return Some(pipeline_marker.complete_err(p, SyntaxError::PipelineNeedsSink));
return Some(pipeline_marker.error(p, SyntaxError::PipelineNeedsSink));
}
if !pipe(p) {
return Some(pipeline_marker.complete(p, PIPELINE));

View file

@ -1,3 +1,4 @@
use clap::builder;
use owo_colors::{unset_override, OwoColorize};
use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken};
use std::mem;
@ -7,7 +8,10 @@ use crate::lst_parser::{
syntax_kind::{Lang, SyntaxKind},
};
use super::{error::SyntaxError, events::Event};
use super::{
error::SyntaxError,
events::{Event, NodeKind},
};
pub struct Output {
pub green_node: GreenNode,
@ -23,6 +27,7 @@ impl std::fmt::Debug for Output {
}
}
const INDENT_STR: &str = " ";
fn debug_print_green_node(
node: NodeOrToken<&GreenNodeData, &GreenTokenData>,
f: &mut dyn std::fmt::Write,
@ -31,7 +36,7 @@ fn debug_print_green_node(
colored: bool,
) -> std::fmt::Result {
for _ in 0..lvl {
f.write_str(" ")?;
f.write_str(INDENT_STR)?;
}
if !colored {
@ -68,7 +73,7 @@ fn debug_print_green_node(
debug_print_green_node(c, f, lvl + 1, errs, colored)?;
}
for _ in 0..lvl {
f.write_str(" ")?;
f.write_str(INDENT_STR)?;
}
if kind != SyntaxKind::PARSE_ERR {
write!(f, "{}", "}\n".yellow())
@ -123,10 +128,11 @@ impl Output {
}
pub fn from_parser_output(
mut raw_toks: Vec<(SyntaxKind, &str)>,
(mut events, errs): (Vec<Event>, Vec<SyntaxError>),
mut events: Vec<Event>,
) -> Self {
let mut builder = GreenNodeBuilder::new();
let mut fw_parents = Vec::new();
let mut errors = Vec::new();
raw_toks.reverse();
for i in 0..events.len() {
@ -170,8 +176,15 @@ impl Output {
}
for kind in fw_parents.drain(..).rev() {
if kind != SyntaxKind::TOMBSTONE {
builder.start_node(kind.into());
match kind {
NodeKind::Syntax(kind) if kind != SyntaxKind::TOMBSTONE => {
builder.start_node(kind.into())
}
NodeKind::Error(err) => {
errors.push(err);
builder.start_node(SyntaxKind::PARSE_ERR.into())
}
_ => {}
}
}
}
@ -180,13 +193,12 @@ impl Output {
let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap();
builder.token(tok.into(), text);
}),
Event::Error => todo!(),
}
}
Self {
green_node: builder.finish(),
errors: errs,
errors,
}
}
}

View file

@ -27,8 +27,7 @@
pkgs = nixpkgs.legacyPackages.${system};
toolchain = with fenix.packages.${system};
combine [
default.toolchain
rust-analyzer
complete.toolchain
];
in {
default = devenv.lib.mkShell {
@ -39,24 +38,14 @@
config,
...
}: {
# languages.rust = {
# enable = true;
# channel = "nightly";
# components = [
# "rustc"
# "cargo"
# "clippy"
# "rustfmt"
# "rust-src"
# "rust-analyzer"
# ];
# };
pre-commit.hooks = {
clippy.enable = false;
rustfmt.enable = true;
};
env = {
RUST_BACKTRACE = 1;
};
packages = with pkgs; [
just
nushell