use std::{ convert::Infallible, ops::{ControlFlow, FromResidual, Try}, }; use crate::ir::FilePos; use super::{Node, ParserCtx, ParserMsg}; pub enum ParseResult { Ok(T), Recover(T), Err(ParserMsg), SubErr, } impl ParseResult { pub fn from_recover(data: T, recover: bool) -> Self { if recover { Self::Recover(data) } else { Self::Ok(data) } } } impl Try for ParseResult { type Output = T; type Residual = Option; fn from_output(output: Self::Output) -> Self { Self::Ok(output) } fn branch(self) -> ControlFlow { match self { ParseResult::Ok(v) => ControlFlow::Continue(v), // TODO: this is messed up; need to break w a Result> or smth :woozy: ParseResult::Recover(v) => ControlFlow::Break(None), ParseResult::Err(e) => ControlFlow::Break(Some(e)), ParseResult::SubErr => ControlFlow::Break(None), } } } impl FromResidual for ParseResult { fn from_residual(residual: ::Residual) -> Self { match residual { Some(err) => Self::Err(err), None => Self::SubErr, } } } impl FromResidual> for ParseResult { fn from_residual(residual: Result) -> Self { match residual { Err(e) => Self::Err(e), } } } impl FromResidual> for ParseResult { fn from_residual(residual: ParseResult) -> Self { match residual { ParseResult::Err(e) => Self::Err(e), ParseResult::SubErr => Self::SubErr, _ => unreachable!(), } } } pub struct NodeParseResult { pub node: Node, pub recover: bool, } impl NodeParseResult { pub fn map) -> U, U>(self, op: F) -> ParseResult { let res = op(self.node); if self.recover { ParseResult::Recover(res) } else { ParseResult::Ok(res) } } } impl Try for NodeParseResult { type Output = Node; type Residual = ParseResult; fn from_output(output: Self::Output) -> Self { Self { node: output, recover: false, } } fn branch(self) -> ControlFlow { if self.recover { ControlFlow::Break(ParseResult::SubErr) } else { ControlFlow::Continue(self.node) } } } impl FromResidual for NodeParseResult { fn from_residual(_: ::Residual) -> Self { // I hope this is unreachable ??? unreachable!() } } pub trait Parsable: Sized { fn parse(ctx: &mut ParserCtx) -> ParseResult; } pub trait MaybeParsable: Sized { fn maybe_parse(ctx: &mut ParserCtx) -> Result, ParserMsg>; } impl Node { pub fn parse(ctx: &mut ParserCtx) -> NodeParseResult { let start = ctx.peek().map(|t| t.span.start).unwrap_or(FilePos::start()); let (inner, recover) = match T::parse(ctx) { ParseResult::Ok(v) => (Some(v), false), ParseResult::Recover(v) => (Some(v), true), ParseResult::Err(e) => { ctx.err(e); (None, true) } ParseResult::SubErr => (None, true), }; let end = ctx.prev_end(); NodeParseResult { node: Self { inner, span: start.to(end), }, recover, } } } impl Node { pub fn maybe_parse(ctx: &mut ParserCtx) -> Option { let start = ctx.next_start(); let inner = match T::maybe_parse(ctx) { Ok(v) => Some(v?), Err(e) => { ctx.err(e); None } }; let end = ctx.prev_end(); Some(Self { inner, span: start.to(end), }) } } pub trait NodeParsable { fn parse_node(ctx: &mut ParserCtx) -> NodeParseResult where Self: Sized; } impl NodeParsable for T { fn parse_node(ctx: &mut ParserCtx) -> NodeParseResult where Self: Sized, { Node::::parse(ctx) } }