1#![allow(clippy::result_large_err)]
13
14use core::str::FromStr;
15use haitaka_types::Move;
16use pest::Parser; use pest::error::Error as PestError;
18use pest::iterators::{Pair, Pairs};
19use pest_derive::Parser; use std::fmt::Debug;
21use std::time::Duration;
22
23use crate::engine::{
24 BestMoveParams, EngineMessage, IdParams, InfoParam, OptionParam, ScoreBound, StatusCheck,
25};
26use crate::gui::{EngineParams, GameStatus, GuiMessage, MateParam};
27
28#[derive(Parser)]
29#[grammar = "usi.pest"]
30struct UsiParser;
31
32pub fn dbg(s: &str) {
34 let res = UsiParser::parse(Rule::start, s);
35 if let Ok(pairs) = res {
36 println!("{:#?}", pairs);
37 } else {
38 println!("{:#?}", res);
39 }
40}
41
42macro_rules! as_str {
47 ($sp:ident) => {
48 $sp.as_span().as_str().trim()
49 };
50}
51
52macro_rules! as_string {
55 ($sp:ident) => {
56 $sp.as_span().as_str().trim().to_string()
57 };
58}
59
60macro_rules! convert_empty {
62 ($s:ident) => {
63 if $s.eq_ignore_ascii_case("<empty>") {
64 Some(String::from(""))
65 } else {
66 Some(String::from($s))
67 }
68 };
69}
70
71macro_rules! as_move {
73 ($sp:ident) => {
74 Move::from_str(as_str!($sp)).unwrap()
75 };
76}
77
78impl GuiMessage {
79 pub fn parse(input: &str) -> Result<Self, PestError<Rule>> {
99 match UsiParser::parse(Rule::start, input) {
100 Ok(pairs) => Ok(Self::inner_parse(pairs.into_iter().next().unwrap())),
101 Err(err) => Err(err),
102 }
103 }
104
105 pub fn parse_first_valid(input: &str) -> Option<Self> {
121 GuiMessageStream::new(input).find(|msg| !matches!(msg, GuiMessage::Unknown(_)))
122 }
123
124 fn inner_parse(p: Pair<'_, Rule>) -> Self {
125 match p.as_rule() {
126 Rule::usi => Self::parse_usi(),
127 Rule::debug => Self::parse_debug(p),
128 Rule::isready => Self::parse_isready(),
129 Rule::setoption => Self::parse_setoption(p),
130 Rule::register_user => Self::parse_register(p),
131 Rule::usinewgame => Self::parse_usinewgame(),
132 Rule::position => Self::parse_position(p),
133 Rule::go => Self::parse_go(p),
134 Rule::stop => Self::parse_stop(),
135 Rule::ponderhit => Self::parse_ponderhit(),
136 Rule::gameover => Self::parse_gameover(p),
137 Rule::quit => Self::parse_quit(),
138 _ => Self::parse_unknown(p.as_str()),
139 }
140 }
141
142 fn parse_unknown(s: &str) -> Self {
144 Self::Unknown(s.to_owned())
145 }
146
147 fn parse_usi() -> Self {
149 Self::Usi
150 }
151
152 fn parse_debug(pair: Pair<Rule>) -> Self {
154 let on = !as_string!(pair).ends_with("off");
155 Self::Debug(on)
156 }
157
158 fn parse_isready() -> Self {
160 Self::IsReady
161 }
162
163 fn parse_setoption(pair: Pair<Rule>) -> Self {
165 let mut name: String = String::default();
166 let mut value: Option<String> = None;
167 for sp in pair.into_inner() {
168 match sp.as_rule() {
169 Rule::setoption_name => {
170 name = as_string!(sp);
171 }
172 Rule::setoption_value => {
173 value = Some(as_string!(sp));
174 }
175 _ => unreachable!(),
176 }
177 }
178 Self::SetOption { name, value }
179 }
180
181 fn parse_register(pair: Pair<Rule>) -> Self {
183 let mut name: Option<String> = None;
184 let mut code: Option<String> = None;
185 for sp in pair.into_inner() {
186 match sp.as_rule() {
187 Rule::register_later => {}
188 Rule::register_with_name_and_code => {
189 for spi in sp.into_inner() {
190 match spi.as_rule() {
191 Rule::register_name => {
192 name = Some(as_string!(spi));
193 }
194 Rule::register_code => {
195 code = Some(as_string!(spi));
196 }
197 _ => unreachable!(),
198 }
199 }
200 }
201 _ => unreachable!(),
202 }
203 }
204 Self::Register { name, code }
205 }
206
207 fn parse_usinewgame() -> Self {
209 Self::UsiNewGame
210 }
211
212 fn parse_position(pair: Pair<Rule>) -> Self {
214 let mut sfen: Option<String> = None;
215 let mut moves: Option<Vec<Move>> = None;
216 for sp in pair.into_inner() {
217 match sp.as_rule() {
218 Rule::startpos => {
219 assert!(sfen.is_none());
220 }
221 Rule::sfenpos => {
222 sfen = Some(
223 as_str!(sp)
224 .strip_prefix("sfen ")
225 .unwrap()
226 .trim()
227 .to_string(),
228 );
229 }
230 Rule::moves => {
231 moves = Some(parse_moves(sp));
232 }
233 _ => unreachable!(),
234 }
235 }
236 Self::Position { sfen, moves }
237 }
238
239 fn parse_go(pair: Pair<Rule>) -> Self {
241 let mut params = EngineParams::new();
242
243 for sp in pair.into_inner() {
244 match sp.as_rule() {
245 Rule::searchmoves => {
246 params = params.searchmoves(parse_moves(sp));
247 }
248 Rule::depth => {
249 params = params.depth(parse_digits::<u16>(sp));
250 }
251 Rule::nodes => {
252 params = params.nodes(parse_digits::<u32>(sp));
253 }
254 Rule::mate => {
255 for spi in sp.into_inner() {
256 match spi.as_rule() {
257 Rule::millisecs => {
258 params = params.mate(MateParam::Timeout(parse_millisecs(spi)))
259 }
260 Rule::infinite => params = params.mate(MateParam::Infinite),
261 _ => unreachable!(),
262 }
263 }
264 }
265 Rule::byoyomi => {
266 params = params.byoyomi(parse_millisecs(sp));
267 }
268 Rule::btime => {
269 params = params.btime(parse_millisecs(sp));
270 }
271 Rule::wtime => {
272 params = params.wtime(parse_millisecs(sp));
273 }
274 Rule::binc => {
275 params = params.binc(parse_millisecs(sp));
276 }
277 Rule::winc => {
278 params = params.winc(parse_millisecs(sp));
279 }
280 Rule::movestogo => {
281 params = params.movestogo(parse_digits::<u16>(sp));
282 }
283 Rule::ponder => {
284 params = params.ponder();
285 }
286 Rule::movetime => params = params.movetime(parse_millisecs(sp)),
287 Rule::infinite => {
288 params = params.infinite();
289 }
290 _ => unreachable!(),
291 }
292 }
293 Self::Go(params)
294 }
295
296 fn parse_stop() -> Self {
298 Self::Stop
299 }
300
301 fn parse_ponderhit() -> Self {
303 Self::PonderHit
304 }
305
306 fn parse_gameover(pair: Pair<Rule>) -> Self {
308 if let Some(sp) = pair.into_inner().next() {
309 match sp.as_rule() {
310 Rule::win => return Self::GameOver(GameStatus::Win),
311 Rule::lose => return Self::GameOver(GameStatus::Lose),
312 Rule::draw => return Self::GameOver(GameStatus::Draw),
313 _ => unreachable!(),
314 }
315 }
316 unreachable!()
317 }
318
319 fn parse_quit() -> Self {
321 Self::Quit
322 }
323}
324
325pub struct GuiMessageStream<'a> {
327 pairs: Pairs<'a, Rule>,
329}
330
331impl<'a> GuiMessageStream<'a> {
332 pub fn new(input: &'a str) -> Self {
336 Self::parse(input)
337 }
338
339 pub fn parse(input: &'a str) -> Self {
343 Self::try_parse(input).expect("Internal error: Failed to initialize UsiParser.")
344 }
345
346 pub fn try_parse(input: &'a str) -> Result<Self, PestError<Rule>> {
347 let pairs = UsiParser::parse(Rule::start, input);
348 match pairs {
349 Ok(pairs) => Ok(Self { pairs }),
350 Err(err) => Err(err),
351 }
352 }
353}
354
355impl Iterator for GuiMessageStream<'_> {
356 type Item = GuiMessage;
357
358 fn next(&mut self) -> Option<Self::Item> {
359 if let Some(pair) = self.pairs.by_ref().next() {
360 let res = GuiMessage::inner_parse(pair);
361 return Some(res);
362 }
363 None
364 }
365}
366
367impl EngineMessage {
370 pub fn parse(input: &str) -> Result<Self, PestError<Rule>> {
404 match UsiParser::parse(Rule::start, input) {
405 Ok(pairs) => Ok(Self::inner_parse(pairs.into_iter().next().unwrap())),
406 Err(err) => Err(err),
407 }
408 }
409
410 pub fn parse_first_valid(input: &str) -> Option<Self> {
418 EngineMessageStream::new(input).find(|msg| !matches!(msg, EngineMessage::Unknown(_)))
419 }
420
421 fn inner_parse(p: Pair<'_, Rule>) -> Self {
422 match p.as_rule() {
423 Rule::id => Self::parse_id(p),
424 Rule::usiok => Self::parse_usiok(),
425 Rule::readyok => Self::parse_readyok(),
426 Rule::bestmove => Self::parse_bestmove(p),
427 Rule::copyprotection => Self::parse_copyprotection(p),
428 Rule::registration => Self::parse_registration(p),
429 Rule::option => Self::parse_option(p),
430 Rule::info => Self::parse_info(p),
431 _ => Self::parse_unknown(p.as_str()),
432 }
433 }
434
435 fn parse_unknown(s: &str) -> Self {
437 Self::Unknown(s.to_owned())
438 }
439
440 fn parse_id(pair: Pair<Rule>) -> Self {
442 if let Some(sp) = pair.into_inner().next() {
443 match sp.as_rule() {
444 Rule::id_name => return EngineMessage::Id(IdParams::Name(parse_tokens(sp))),
445 Rule::id_author => return EngineMessage::Id(IdParams::Author(parse_tokens(sp))),
446 _ => unreachable!(),
447 }
448 }
449 unreachable!()
450 }
451
452 fn parse_usiok() -> Self {
454 EngineMessage::UsiOk
455 }
456
457 fn parse_readyok() -> Self {
459 EngineMessage::ReadyOk
460 }
461
462 fn parse_bestmove(pair: Pair<Rule>) -> Self {
464 let mut bestmove: Option<Move> = None;
465 let mut ponder: Option<Move> = None;
466
467 for sp in pair.into_inner() {
468 match sp.as_rule() {
469 Rule::one_move => {
470 bestmove = Some(as_move!(sp));
471 }
472 Rule::ponder_move => {
473 ponder = Some(parse_move(sp));
474 }
475 Rule::resign => return EngineMessage::BestMove(BestMoveParams::Resign),
476 Rule::win => return EngineMessage::BestMove(BestMoveParams::Win),
477 _ => unreachable!(),
478 }
479 }
480
481 if let Some(bestmove) = bestmove {
482 EngineMessage::BestMove(BestMoveParams::BestMove { bestmove, ponder })
483 } else {
484 unreachable!()
485 }
486 }
487
488 fn parse_copyprotection(pair: Pair<Rule>) -> Self {
490 let state = Self::parse_status_check(pair);
491 EngineMessage::CopyProtection(state)
492 }
493
494 fn parse_registration(pair: Pair<Rule>) -> Self {
496 let state = Self::parse_status_check(pair);
497 EngineMessage::Registration(state)
498 }
499
500 fn parse_status_check(pair: Pair<Rule>) -> StatusCheck {
501 for sp in pair.into_inner() {
502 if let Rule::status_check = sp.as_rule() {
503 let s = as_str!(sp);
504 match s {
505 "checking" => return StatusCheck::Checking,
506 "ok" => return StatusCheck::Ok,
507 "error" => return StatusCheck::Error,
508 _ => unreachable!(),
509 };
510 }
511 }
512 unreachable!()
513 }
514
515 fn parse_option(pair: Pair<Rule>) -> Self {
517 if let Some(sp) = pair.into_inner().next() {
518 match sp.as_rule() {
519 Rule::check_option => return Self::parse_check_option(sp),
520 Rule::spin_option => return Self::parse_spin_option(sp),
521 Rule::combo_option => return Self::parse_combo_option(sp),
522 Rule::string_option => return Self::parse_string_option(sp),
523 Rule::button_option => return Self::parse_button_option(sp),
524 Rule::filename_option => return Self::parse_filename_option(sp),
525 _ => unreachable!(),
526 }
527 }
528 unreachable!()
529 }
530
531 fn parse_check_option(pair: Pair<Rule>) -> Self {
533 let mut name: Option<String> = None;
534 let mut default: Option<bool> = None;
535 for sp in pair.into_inner() {
536 match sp.as_rule() {
537 Rule::option_name => name = Some(parse_tokens(sp)),
538 Rule::check_default => default = Some(as_string!(sp).eq_ignore_ascii_case("true")),
539 _ => (),
540 }
541 }
542 if let Some(name) = name {
543 Self::Option(OptionParam::Check { name, default })
544 } else {
545 unreachable!()
546 }
547 }
548
549 fn parse_spin_option(pair: Pair<Rule>) -> Self {
551 let mut name: Option<String> = None;
552 let mut default: Option<i32> = None;
553 let mut min: Option<i32> = None;
554 let mut max: Option<i32> = None;
555
556 for sp in pair.into_inner() {
557 match sp.as_rule() {
558 Rule::option_name => name = Some(parse_tokens(sp)),
559 Rule::spin_default => default = Some(parse_integer::<i32>(sp)),
560 Rule::spin_min => min = Some(parse_integer::<i32>(sp)),
561 Rule::spin_max => max = Some(parse_integer::<i32>(sp)),
562 _ => (),
563 }
564 }
565
566 if let Some(name) = name {
567 Self::Option(OptionParam::Spin {
568 name,
569 default,
570 min,
571 max,
572 })
573 } else {
574 unreachable!()
575 }
576 }
577
578 fn parse_combo_option(pair: Pair<Rule>) -> Self {
580 let mut name: Option<String> = None;
581 let mut default: Option<String> = None;
582 let mut vars: Vec<String> = Vec::new();
583
584 for sp in pair.into_inner() {
585 match sp.as_rule() {
586 Rule::option_name => name = Some(parse_tokens(sp)),
587 Rule::combo_default => default = Some(parse_tokens(sp)),
588 Rule::var_token => vars.push(parse_tokens(sp)),
589 _ => (),
590 }
591 }
592
593 if let Some(name) = name {
594 Self::Option(OptionParam::Combo {
595 name,
596 default,
597 vars,
598 })
599 } else {
600 unreachable!()
601 }
602 }
603
604 fn parse_string_option(pair: Pair<Rule>) -> Self {
606 let mut name: Option<String> = None;
607 let mut default: Option<String> = None;
608
609 for sp in pair.into_inner() {
610 match sp.as_rule() {
611 Rule::option_name => name = Some(parse_tokens(sp)),
612 Rule::token => {
613 default = {
614 let s = as_string!(sp);
615 convert_empty!(s)
616 }
617 }
618 _ => (),
619 }
620 }
621
622 if let Some(name) = name {
623 Self::Option(OptionParam::String { name, default })
624 } else {
625 unreachable!()
626 }
627 }
628
629 fn parse_button_option(pair: Pair<Rule>) -> Self {
631 for sp in pair.into_inner() {
632 if sp.as_rule() == Rule::option_name {
633 let name = parse_tokens(sp);
634 return Self::Option(OptionParam::Button { name });
635 }
636 }
637 unreachable!()
638 }
639
640 fn parse_filename_option(pair: Pair<Rule>) -> Self {
642 let mut name: Option<String> = None;
643 let mut default: Option<String> = None;
644
645 for sp in pair.into_inner() {
646 match sp.as_rule() {
647 Rule::option_name => name = Some(parse_tokens(sp)),
648 Rule::token => {
649 default = {
650 let s = as_string!(sp);
651 convert_empty!(s)
652 }
653 }
654 _ => (),
655 }
656 }
657
658 if let Some(name) = name {
659 Self::Option(OptionParam::Filename { name, default })
660 } else {
661 unreachable!()
662 }
663 }
664
665 fn parse_info(pair: Pair<Rule>) -> Self {
667 let mut v: Vec<InfoParam> = Vec::<InfoParam>::new();
668 for sp in pair.into_inner() {
669 let info: InfoParam = match sp.as_rule() {
670 Rule::info_depth => InfoParam::Depth(parse_digits::<u16>(sp)),
671 Rule::info_seldepth => InfoParam::SelDepth(parse_digits::<u16>(sp)),
672 Rule::info_time => InfoParam::Time(parse_millisecs(sp)),
673 Rule::info_nodes => InfoParam::Nodes(parse_digits::<u64>(sp)),
674 Rule::info_currmovenumber => InfoParam::CurrMoveNumber(parse_digits::<u16>(sp)),
675 Rule::info_currmove => InfoParam::CurrMove(parse_move(sp)),
676 Rule::info_hashfull => InfoParam::HashFull(parse_digits::<u16>(sp)),
677 Rule::info_nps => InfoParam::Nps(parse_digits::<u64>(sp)),
678 Rule::info_cpuload => InfoParam::CpuLoad(parse_digits::<u16>(sp)),
679 Rule::info_multipv => InfoParam::MultiPv(parse_digits::<u16>(sp)),
680 Rule::info_string => InfoParam::String(parse_tokens(sp)),
681 Rule::info_pv => InfoParam::Pv(parse_moves(sp)),
682 Rule::info_refutation => InfoParam::Refutation(parse_moves(sp)),
683 Rule::info_currline => Self::parse_currline(sp),
684 Rule::info_score_cp => Self::parse_score_cp(sp),
685 Rule::info_score_mate => Self::parse_score_mate(sp),
686 _ => unreachable!(),
687 };
688 v.push(info);
689 }
690 EngineMessage::Info(v)
691 }
692
693 fn parse_currline(pair: Pair<Rule>) -> InfoParam {
695 let mut cpu_nr: Option<u16> = None;
696 let mut line: Vec<Move> = Vec::<Move>::new();
697
698 for sp in pair.into_inner() {
699 match sp.as_rule() {
700 Rule::cpunr => cpu_nr = Some(parse_digits::<u16>(sp)),
701 Rule::moves => line = parse_moves(sp),
702 _ => unreachable!(),
703 }
704 }
705 InfoParam::CurrLine { cpu_nr, line }
706 }
707
708 fn parse_score_cp(pair: Pair<Rule>) -> InfoParam {
710 let mut v: Option<i32> = None;
711 let mut bound: ScoreBound = ScoreBound::Exact;
712
713 for sp in pair.into_inner() {
714 match sp.as_rule() {
715 Rule::integer => {
716 let s = as_str!(sp); v = Some(s.parse::<i32>().unwrap_or_else(|err| {
718 unreachable!(
719 "PEST grammar bug: failed to parse integer '{}': {:?}",
720 s, err
721 )
722 }));
723 }
724 Rule::lowerbound => bound = ScoreBound::Lower,
725 Rule::upperbound => bound = ScoreBound::Upper,
726 _ => unreachable!(), }
728 }
729
730 if let Some(value) = v {
731 InfoParam::ScoreCp(value, bound)
732 } else {
733 unreachable!()
734 }
735 }
736
737 fn parse_score_mate(pair: Pair<Rule>) -> InfoParam {
739 let mut v: Option<i32> = None;
740 let mut bound: ScoreBound = ScoreBound::Exact;
741
742 for sp in pair.into_inner() {
743 match sp.as_rule() {
744 Rule::integer => {
745 let s = as_str!(sp); v = Some(s.parse::<i32>().unwrap());
747 }
748 Rule::plus => bound = ScoreBound::MatePlus,
749 Rule::minus => bound = ScoreBound::MateMin,
750 Rule::lowerbound => bound = ScoreBound::Lower,
751 Rule::upperbound => bound = ScoreBound::Upper,
752 _ => unreachable!(),
753 }
754 }
755 InfoParam::ScoreMate(v, bound)
756 }
757}
758
759pub struct EngineMessageStream<'a> {
761 pairs: Pairs<'a, Rule>,
763}
764
765impl<'a> EngineMessageStream<'a> {
766 pub fn new(input: &'a str) -> Self {
770 Self::parse(input)
771 }
772
773 pub fn parse(input: &'a str) -> Self {
777 Self::try_parse(input).expect("Internal error: Failed to initialize UsiParser.")
778 }
779
780 pub fn try_parse(input: &'a str) -> Result<Self, PestError<Rule>> {
781 let pairs = UsiParser::parse(Rule::start, input);
782 match pairs {
783 Ok(pairs) => Ok(Self { pairs }),
784 Err(err) => Err(err),
785 }
786 }
787}
788
789impl Iterator for EngineMessageStream<'_> {
790 type Item = EngineMessage;
791
792 fn next(&mut self) -> Option<Self::Item> {
793 if let Some(pair) = self.pairs.by_ref().next() {
794 let res = EngineMessage::inner_parse(pair);
795 return Some(res);
796 }
797 None
798 }
799}
800
801fn parse_move(pair: Pair<Rule>) -> Move {
808 for sp in pair.into_inner() {
809 if let Rule::one_move = sp.as_rule() {
810 return as_move!(sp);
811 }
812 }
813 unreachable!()
814}
815
816fn parse_moves(pair: Pair<Rule>) -> Vec<Move> {
817 let mut moves = Vec::<Move>::new();
818
819 for sp in pair.into_inner() {
820 match sp.as_rule() {
821 Rule::one_move => {
822 moves.push(as_move!(sp));
823 }
824 Rule::moves => {
825 let mvs: Vec<Move> = parse_moves(sp);
826 moves.extend(mvs);
827 }
828 _ => unreachable!(),
829 }
830 }
831
832 moves
833}
834
835fn parse_digits<T>(pair: Pair<Rule>) -> T
836where
837 T: FromStr,
838 T::Err: Debug,
839{
840 for sp in pair.into_inner() {
841 if let Rule::digits = sp.as_rule() {
842 return as_str!(sp).parse::<T>().unwrap();
843 }
844 }
845 unreachable!()
846}
847
848fn parse_integer<T>(pair: Pair<Rule>) -> T
849where
850 T: FromStr,
851 T::Err: Debug,
852{
853 for sp in pair.into_inner() {
854 if let Rule::integer = sp.as_rule() {
855 return as_str!(sp).parse::<T>().unwrap();
856 }
857 }
858 unreachable!()
859}
860
861fn parse_millisecs(pair: Pair<Rule>) -> Duration {
862 for sp in pair.into_inner() {
863 if let Rule::millisecs = sp.as_rule() {
864 let milliseconds: u64 = as_str!(sp).parse::<u64>().unwrap();
865 return Duration::from_millis(milliseconds);
866 }
867 if let Rule::digits = sp.as_rule() {
868 let milliseconds: u64 = as_str!(sp).parse::<u64>().unwrap();
869 return Duration::from_millis(milliseconds);
870 }
871 }
872 unreachable!()
873}
874
875fn parse_tokens(pair: Pair<'_, Rule>) -> String {
876 if let Some(sp) = pair.into_inner().next() {
877 match sp.as_rule() {
878 Rule::tokens => return as_string!(sp).to_owned(),
879 Rule::token => return as_string!(sp).to_owned(),
880 _ => return parse_tokens(sp),
881 }
882 }
883 unreachable!()
884}