use std::{cell::RefCell, marker::PhantomData, rc::Rc};
pub mod command;
pub mod lex;
pub mod model;
pub mod parse;
pub mod prelude;
pub mod rng;
pub mod unparse;
use thiserror::Error;
#[cfg(feature = "diagnostics")]
use ariadne::Report;
#[cfg(feature = "diagnostics")]
use crate::diagnostics::{SimpleSource, ToAriadne};
use self::{
lex::{LexOutput, LexWarningWithRange},
model::Bms,
parse::{
ParseErrorWithRange, ParseWarningWithRange,
check_playing::{PlayingCheckOutput, PlayingError, PlayingWarning},
token_processor::{
DefaultTokenRelaxer, NoopTokenModifier, SequentialTokenModifier, TokenModifier,
TokenProcessor, full_preset,
},
},
prelude::*,
};
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BmsWarning {
#[error("Warn: lex: {0}")]
Lex(#[from] LexWarningWithRange),
#[error("Warn: parse: {0}")]
Parse(#[from] ParseWarningWithRange),
#[error("Warn: playing: {0}")]
PlayingWarning(#[from] PlayingWarning),
#[error("Error: playing: {0}")]
PlayingError(#[from] PlayingError),
}
#[must_use]
pub struct ParseConfig<T, P, R, M> {
key_mapper: PhantomData<fn() -> T>,
prompter: P,
rng: R,
token_modifier: M,
}
#[cfg(feature = "rand")]
pub fn default_config() -> ParseConfig<
KeyLayoutBeat,
AlwaysWarnAndUseNewer,
RandRng<rand::rngs::StdRng>,
DefaultTokenRelaxer,
> {
ParseConfig {
key_mapper: PhantomData,
prompter: AlwaysWarnAndUseNewer,
rng: RandRng(rand::make_rng()),
token_modifier: DefaultTokenRelaxer,
}
}
#[cfg(not(feature = "rand"))]
pub fn default_config()
-> ParseConfig<KeyLayoutBeat, AlwaysWarnAndUseNewer, rng::JavaRandom, DefaultTokenRelaxer> {
ParseConfig {
key_mapper: PhantomData,
prompter: AlwaysWarnAndUseNewer,
rng: rng::JavaRandom::default(),
token_modifier: DefaultTokenRelaxer,
}
}
pub fn default_config_with_rng<R>(
rng: R,
) -> ParseConfig<KeyLayoutBeat, AlwaysWarnAndUseNewer, R, DefaultTokenRelaxer> {
ParseConfig {
key_mapper: PhantomData,
prompter: AlwaysWarnAndUseNewer,
rng,
token_modifier: DefaultTokenRelaxer,
}
}
impl<T, P, R, M> ParseConfig<T, P, R, M> {
pub fn key_mapper<T2: KeyLayoutMapper>(self) -> ParseConfig<T2, P, R, M> {
ParseConfig {
key_mapper: PhantomData,
prompter: self.prompter,
rng: self.rng,
token_modifier: self.token_modifier,
}
}
pub fn prompter<P2: Prompter>(self, prompter: P2) -> ParseConfig<T, P2, R, M> {
ParseConfig {
key_mapper: PhantomData,
prompter,
rng: self.rng,
token_modifier: self.token_modifier,
}
}
pub fn rng<R2: Rng>(self, rng: R2) -> ParseConfig<T, P, R2, M> {
ParseConfig {
key_mapper: PhantomData,
prompter: self.prompter,
rng,
token_modifier: self.token_modifier,
}
}
pub fn append_token_modifier<M2: TokenModifier>(
self,
token_modifier: M2,
) -> ParseConfig<T, P, R, SequentialTokenModifier<M, M2>>
where
M: TokenModifier,
{
ParseConfig {
key_mapper: PhantomData,
prompter: self.prompter,
rng: self.rng,
token_modifier: self.token_modifier.then(token_modifier),
}
}
pub fn override_token_modifier<M2: TokenModifier>(
self,
token_modifier: M2,
) -> ParseConfig<T, P, R, M2> {
ParseConfig {
key_mapper: PhantomData,
prompter: self.prompter,
rng: self.rng,
token_modifier,
}
}
pub fn map_token_modifier<F, M2>(self, f: F) -> ParseConfig<T, P, R, M2>
where
F: FnOnce(M) -> M2,
{
ParseConfig {
key_mapper: PhantomData,
prompter: self.prompter,
rng: self.rng,
token_modifier: f(self.token_modifier),
}
}
pub fn clean_token_modifier(self) -> ParseConfig<T, P, R, NoopTokenModifier> {
self.override_token_modifier(NoopTokenModifier)
}
pub(crate) fn build(self) -> (impl TokenProcessor<Output = Bms>, P)
where
T: KeyLayoutMapper,
P: Prompter,
R: Rng,
{
struct AggregateTokenProcessor<T, R> {
key_mapper: PhantomData<fn() -> T>,
rng: Rc<RefCell<R>>,
}
impl<T: KeyLayoutMapper, R: Rng> TokenProcessor for AggregateTokenProcessor<T, R> {
type Output = Bms;
fn process<P: Prompter>(
&self,
ctx: &mut parse::token_processor::ProcessContext<'_, '_, P>,
) -> Result<Self::Output, ParseErrorWithRange> {
full_preset::<T, R>(Rc::clone(&self.rng)).process(ctx)
}
}
(
AggregateTokenProcessor::<T, R> {
key_mapper: PhantomData,
rng: Rc::new(RefCell::new(self.rng)),
},
self.prompter,
)
}
}
pub fn parse_bms<T: KeyLayoutMapper, P: Prompter, R: Rng, M: TokenModifier>(
source: &str,
config: ParseConfig<T, P, R, M>,
) -> BmsOutput {
let LexOutput {
mut tokens,
lex_warnings,
} = lex::TokenStream::parse_lex(source);
let mut warnings: Vec<BmsWarning> = lex_warnings.into_iter().map(BmsWarning::Lex).collect();
config.token_modifier.modify(&mut tokens);
let parse_output = Bms::from_token_stream::<'_, T, _, _, _>(&tokens, config);
let bms_result = parse_output.bms;
warnings.extend(
parse_output
.parse_warnings
.into_iter()
.map(BmsWarning::Parse),
);
if let Ok(ref bms) = bms_result {
let PlayingCheckOutput {
playing_warnings,
playing_errors,
} = bms.check_playing::<T>();
warnings.extend(playing_warnings.into_iter().map(BmsWarning::PlayingWarning));
warnings.extend(playing_errors.into_iter().map(BmsWarning::PlayingError));
}
BmsOutput {
bms: bms_result,
warnings,
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[must_use]
pub struct BmsOutput {
pub bms: Result<Bms, ParseErrorWithRange>,
pub warnings: Vec<BmsWarning>,
}
#[cfg(feature = "diagnostics")]
impl ToAriadne for BmsWarning {
fn to_report<'a>(
&self,
src: &SimpleSource<'a>,
) -> Report<'a, (String, std::ops::Range<usize>)> {
use BmsWarning::{Lex, Parse, PlayingError, PlayingWarning};
match self {
Lex(e) => e.to_report(src),
Parse(e) => e.to_report(src),
PlayingWarning(w) => w.to_report(src),
PlayingError(e) => e.to_report(src),
}
}
}