Skip to main content

pspp/
command.rs

1// PSPP - a program for statistical analysis.
2// Copyright (C) 2025 Free Software Foundation, Inc.
3//
4// This program is free software: you can redistribute it and/or modify it under
5// the terms of the GNU General Public License as published by the Free Software
6// Foundation, either version 3 of the License, or (at your option) any later
7// version.
8//
9// This program is distributed in the hope that it will be useful, but WITHOUT
10// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
12// details.
13//
14// You should have received a copy of the GNU General Public License along with
15// this program.  If not, see <http://www.gnu.org/licenses/>.
16
17#![allow(dead_code)]
18use std::{
19    fmt::{Debug, Write},
20    ops::RangeFrom,
21    sync::OnceLock,
22};
23
24use crosstabs::crosstabs_command;
25use ctables::ctables_command;
26use data_list::data_list_command;
27use descriptives::descriptives_command;
28use either::Either;
29use enumset::{EnumSet, EnumSetType};
30use pspp_derive::FromTokens;
31
32use crate::{
33    format::AbstractFormat,
34    identifier::Identifier,
35    integer::ToInteger,
36    lex::{
37        Punct, Token,
38        command_name::CommandMatcher,
39        lexer::{LexToken, TokenSlice},
40    },
41    message::{Diagnostic, Diagnostics},
42};
43
44pub mod crosstabs;
45pub mod ctables;
46pub mod data_list;
47pub mod descriptives;
48
49#[derive(Debug, EnumSetType)]
50enum State {
51    /// No active dataset yet defined.
52    Initial,
53
54    /// Active dataset has been defined.
55    Data,
56
57    /// Inside `INPUT PROGRAM`.
58    InputProgram,
59
60    /// Inside `FILE TYPE`.
61    FileType,
62
63    /// State nested inside `LOOP` or `DO IF`, inside [State::Data].
64    NestedData,
65
66    /// State nested inside `LOOP` or `DO IF`, inside [State::InputProgram].
67    NestedInputProgram,
68}
69
70struct Command {
71    allowed_states: EnumSet<State>,
72    enhanced_only: bool,
73    testing_only: bool,
74    no_abbrev: bool,
75    name: &'static str,
76    run: Box<dyn Fn(&mut Context) + Send + Sync>, //-> Box<dyn ParsedCommand> + Send + Sync>,
77}
78
79#[derive(Debug)]
80enum ParseError {
81    Error(Diagnostics),
82    Mismatch(Diagnostics),
83}
84
85#[derive(Debug)]
86struct Parsed<T> {
87    value: T,
88    rest: TokenSlice,
89    diagnostics: Diagnostics,
90}
91
92impl<T> Parsed<T> {
93    pub fn new(value: T, rest: TokenSlice, warnings: Diagnostics) -> Self {
94        Self {
95            value,
96            rest,
97            diagnostics: warnings,
98        }
99    }
100    pub fn ok(value: T, rest: TokenSlice) -> Self {
101        Self {
102            value,
103            rest,
104            diagnostics: Diagnostics::default(),
105        }
106    }
107    pub fn into_tuple(self) -> (T, TokenSlice, Diagnostics) {
108        (self.value, self.rest, self.diagnostics)
109    }
110    pub fn take_diagnostics(self, d: &mut Diagnostics) -> (T, TokenSlice) {
111        let (value, rest, mut diagnostics) = self.into_tuple();
112        d.0.append(&mut diagnostics.0);
113        (value, rest)
114    }
115    pub fn map<F, R>(self, f: F) -> Parsed<R>
116    where
117        F: FnOnce(T) -> R,
118    {
119        Parsed {
120            value: f(self.value),
121            rest: self.rest,
122            diagnostics: self.diagnostics,
123        }
124    }
125    pub fn warn(self, mut warnings: Diagnostics) -> Self {
126        Self {
127            value: self.value,
128            rest: self.rest,
129            diagnostics: {
130                let mut vec = self.diagnostics.0;
131                vec.append(&mut warnings.0);
132                Diagnostics(vec)
133            },
134        }
135    }
136}
137
138type ParseResult<T> = Result<Parsed<T>, ParseError>;
139
140trait MismatchToError {
141    fn mismatch_to_error(self) -> Self;
142}
143
144impl<T> MismatchToError for ParseResult<T> {
145    fn mismatch_to_error(self) -> Self {
146        match self {
147            Err(ParseError::Mismatch(diagnostic)) => Err(ParseError::Error(diagnostic)),
148            rest => rest,
149        }
150    }
151}
152
153trait FromTokens {
154    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
155    where
156        Self: Sized;
157}
158
159impl<T> FromTokens for Option<T>
160where
161    T: FromTokens,
162{
163    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
164    where
165        Self: Sized,
166    {
167        match T::from_tokens(input) {
168            Ok(p) => Ok(p.map(Some)),
169            Err(ParseError::Mismatch(_)) => Ok(Parsed::ok(None, input.clone())),
170            Err(ParseError::Error(error)) => Err(ParseError::Error(error)),
171        }
172    }
173}
174
175impl<L, R> FromTokens for Either<L, R>
176where
177    L: FromTokens,
178    R: FromTokens,
179{
180    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
181    where
182        Self: Sized,
183    {
184        match L::from_tokens(input) {
185            Ok(p) => Ok(p.map(Either::Left)),
186            Err(ParseError::Mismatch(_)) => Ok(R::from_tokens(input)?.map(Either::Right)),
187            Err(ParseError::Error(error)) => Err(ParseError::Error(error)),
188        }
189    }
190}
191
192impl<A, B> FromTokens for (A, B)
193where
194    A: FromTokens,
195    B: FromTokens,
196{
197    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
198    where
199        Self: Sized,
200    {
201        let (a, input, mut diagnostics) = A::from_tokens(input)?.into_tuple();
202        let (b, rest, mut diagnostics2) = B::from_tokens(&input)?.into_tuple();
203        diagnostics.0.append(&mut diagnostics2.0);
204        Ok(Parsed::new((a, b), rest, diagnostics))
205    }
206}
207
208impl<A, B, C> FromTokens for (A, B, C)
209where
210    A: FromTokens,
211    B: FromTokens,
212    C: FromTokens,
213{
214    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
215    where
216        Self: Sized,
217    {
218        let (a, input, mut diagnostics) = A::from_tokens(input)?.into_tuple();
219        let (b, input, mut diagnostics2) = B::from_tokens(&input)?.into_tuple();
220        let (c, rest, mut diagnostics3) = C::from_tokens(&input)?.into_tuple();
221        diagnostics.0.append(&mut diagnostics2.0);
222        diagnostics.0.append(&mut diagnostics3.0);
223        Ok(Parsed::new((a, b, c), rest, diagnostics))
224    }
225}
226
227#[derive(Debug, pspp_derive::FromTokens)]
228#[pspp(syntax = "/")]
229pub struct Slash;
230
231#[derive(Debug)]
232pub struct Comma;
233
234impl FromTokens for Comma {
235    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
236    where
237        Self: Sized,
238    {
239        _parse_token(input, &Token::Punct(Punct::Comma)).map(|p| p.map(|_| Comma))
240    }
241}
242
243#[derive(Debug, pspp_derive::FromTokens)]
244#[pspp(syntax = "=")]
245pub struct Equals;
246
247#[derive(Debug, pspp_derive::FromTokens)]
248#[pspp(syntax = "&")]
249pub struct And;
250
251#[derive(Debug, pspp_derive::FromTokens)]
252#[pspp(syntax = ">")]
253pub struct Gt;
254
255#[derive(Debug, pspp_derive::FromTokens)]
256#[pspp(syntax = "+")]
257pub struct Plus;
258
259#[derive(Debug, pspp_derive::FromTokens)]
260#[pspp(syntax = "-")]
261pub struct Dash;
262
263#[derive(Debug, pspp_derive::FromTokens)]
264#[pspp(syntax = "*")]
265pub struct Asterisk;
266
267#[derive(Debug, pspp_derive::FromTokens)]
268#[pspp(syntax = "**")]
269pub struct Exp;
270
271#[derive(Debug, pspp_derive::FromTokens)]
272struct By;
273
274pub struct Punctuated<T, P = Option<Comma>> {
275    head: Vec<(T, P)>,
276    tail: Option<T>,
277}
278
279impl<T, P> Debug for Punctuated<T, P>
280where
281    T: Debug,
282{
283    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284        write!(f, "[")?;
285        for (index, item) in self
286            .head
287            .iter()
288            .map(|(t, _p)| t)
289            .chain(self.tail.iter())
290            .enumerate()
291        {
292            if index > 0 {
293                write!(f, ", ")?;
294            }
295            write!(f, "{item:?}")?;
296        }
297        write!(f, "]")
298    }
299}
300
301impl<T, P> FromTokens for Punctuated<T, P>
302where
303    T: FromTokens,
304    P: FromTokens,
305{
306    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
307    where
308        Self: Sized,
309    {
310        let mut head = Vec::new();
311        let mut warnings_vec = Vec::new();
312        let mut input = input.clone();
313        let tail = loop {
314            let t = match T::from_tokens(&input) {
315                Ok(Parsed {
316                    value,
317                    rest,
318                    diagnostics: mut warnings,
319                }) => {
320                    warnings_vec.append(&mut warnings.0);
321                    input = rest;
322                    value
323                }
324                Err(ParseError::Mismatch(_)) => break None,
325                Err(ParseError::Error(e)) => return Err(ParseError::Error(e)),
326            };
327            let p = match P::from_tokens(&input) {
328                Ok(Parsed {
329                    value,
330                    rest,
331                    diagnostics: mut warnings,
332                }) => {
333                    warnings_vec.append(&mut warnings.0);
334                    input = rest;
335                    value
336                }
337                Err(ParseError::Mismatch(_)) => break Some(t),
338                Err(ParseError::Error(e)) => return Err(ParseError::Error(e)),
339            };
340            head.push((t, p));
341        };
342        Ok(Parsed {
343            value: Punctuated { head, tail },
344            rest: input,
345            diagnostics: Diagnostics(warnings_vec),
346        })
347    }
348}
349
350impl<T> FromTokens for Box<T>
351where
352    T: FromTokens,
353{
354    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
355    where
356        Self: Sized,
357    {
358        T::from_tokens(input).map(|p| p.map(|value| Box::new(value)))
359    }
360}
361
362pub struct Subcommands<T>(Vec<T>);
363
364impl<T> Debug for Subcommands<T>
365where
366    T: Debug,
367{
368    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369        write!(f, "Subcommands[")?;
370        for (index, item) in self.0.iter().enumerate() {
371            if index > 0 {
372                writeln!(f, ",")?;
373            }
374            write!(f, "{item:?}")?;
375        }
376        write!(f, "]")
377    }
378}
379
380impl<T> FromTokens for Subcommands<T>
381where
382    T: FromTokens,
383{
384    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
385    where
386        Self: Sized,
387    {
388        let mut items = Vec::new();
389        let mut diagnostics = Vec::new();
390        let mut input = input.clone();
391        loop {
392            let start = input.skip_until(|token| token != &Token::Punct(Punct::Slash));
393            if start.is_empty() {
394                break;
395            }
396            let end = start.skip_to(&Token::Punct(Punct::Slash));
397            let subcommand = start.subslice(0..start.len() - end.len());
398            match T::from_tokens(&subcommand) {
399                Ok(p) => {
400                    let (value, rest, mut d) = p.into_tuple();
401                    items.push(value);
402                    diagnostics.append(&mut d.0);
403                    if !rest.is_empty() {
404                        diagnostics.push(rest.warning("Syntax error expecting end of subcommand."));
405                    }
406                }
407                Err(ParseError::Error(mut d) | ParseError::Mismatch(mut d)) => {
408                    diagnostics.append(&mut d.0);
409                }
410            }
411            input = end;
412        }
413        println!("{diagnostics:?}");
414        Ok(Parsed {
415            value: Subcommands(items),
416            rest: input,
417            diagnostics: Diagnostics(diagnostics),
418        })
419    }
420}
421
422#[derive(Debug)]
423pub struct Seq0<T>(Vec<T>);
424
425impl<T> FromTokens for Seq0<T>
426where
427    T: FromTokens,
428{
429    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
430    where
431        Self: Sized,
432    {
433        let mut values_vec = Vec::new();
434        let mut warnings_vec = Vec::new();
435        let mut input = input.clone();
436        while !input.is_empty() {
437            match T::from_tokens(&input) {
438                Ok(Parsed {
439                    value,
440                    rest,
441                    diagnostics: mut warnings,
442                }) => {
443                    warnings_vec.append(&mut warnings.0);
444                    if input.len() == rest.len() {
445                        break;
446                    }
447                    values_vec.push(value);
448                    input = rest;
449                }
450                Err(ParseError::Mismatch(_)) => break,
451                Err(ParseError::Error(e)) => return Err(ParseError::Error(e)),
452            }
453        }
454        Ok(Parsed {
455            value: Seq0(values_vec),
456            rest: input,
457            diagnostics: Diagnostics(warnings_vec),
458        })
459    }
460}
461
462#[derive(Debug)]
463pub struct Seq1<T>(Vec<T>);
464
465impl<T> FromTokens for Seq1<T>
466where
467    T: FromTokens,
468{
469    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
470    where
471        Self: Sized,
472    {
473        let mut values_vec = Vec::new();
474        let mut warnings_vec = Vec::new();
475        let mut input = input.clone();
476        while !input.is_empty() {
477            match T::from_tokens(&input) {
478                Ok(Parsed {
479                    value,
480                    rest,
481                    diagnostics: mut warnings,
482                }) => {
483                    warnings_vec.append(&mut warnings.0);
484                    if input.len() == rest.len() {
485                        break;
486                    }
487                    values_vec.push(value);
488                    input = rest;
489                }
490                Err(ParseError::Mismatch(_)) => break,
491                Err(ParseError::Error(e)) => return Err(ParseError::Error(e)),
492            }
493        }
494        if values_vec.is_empty() {
495            return Err(ParseError::Mismatch(input.error("Syntax error.").into()));
496        }
497        Ok(Parsed {
498            value: Seq1(values_vec),
499            rest: input,
500            diagnostics: Diagnostics(warnings_vec),
501        })
502    }
503}
504
505/*
506impl<T> FromTokens for Vec<T>
507where
508    T: FromTokens,
509{
510    fn from_tokens(mut input: &TokenSlice) -> ParseResult<Self>
511    where
512        Self: Sized,
513    {
514        let mut values_vec = Vec::new();
515        let mut warnings_vec = Vec::new();
516        while !input.is_empty() {
517            match T::from_tokens(input) {
518                Ok(Parsed {
519                    value,
520                    rest,
521                    diagnostics: mut warnings,
522                }) => {
523                    values_vec.push(value);
524                    warnings_vec.append(&mut warnings.0);
525                    input = rest;
526                }
527                Err(ParseError::Mismatch(_)) => break,
528                Err(ParseError::Error(e)) => return Err(ParseError::Error(e)),
529            }
530        }
531        Ok(Parsed {
532            value: values_vec,
533            rest: input,
534            diagnostics: Diagnostics(warnings_vec),
535        })
536    }
537}*/
538
539impl FromTokens for TokenSlice {
540    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
541    where
542        Self: Sized,
543    {
544        Ok(Parsed::ok(input.clone(), input.end()))
545    }
546}
547
548#[derive(Debug)]
549struct Subcommand<T>(pub T);
550
551impl<T> FromTokens for Subcommand<T>
552where
553    T: FromTokens,
554{
555    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
556    where
557        Self: Sized,
558    {
559        let start = input.skip_until(|token| token != &Token::Punct(Punct::Slash));
560        if start.is_empty() {
561            return Err(ParseError::Error(
562                input.error("Syntax error at end of input.").into(),
563            ));
564        }
565        let end = start.skip_to(&Token::Punct(Punct::Slash));
566        let subcommand = start.subslice(0..start.len() - end.len());
567        let (value, rest, mut warnings) = T::from_tokens(&subcommand)?.into_tuple();
568        if !rest.is_empty() {
569            warnings
570                .0
571                .push(rest.warning("Syntax error expecting end of subcommand."));
572        }
573        Ok(Parsed::new(Self(value), end, warnings))
574    }
575}
576
577#[derive(Debug)]
578struct InParens<T>(pub T);
579
580impl<T> FromTokens for InParens<T>
581where
582    T: FromTokens,
583{
584    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
585    where
586        Self: Sized,
587    {
588        let ((), rest, _) = parse_token(input, &Token::Punct(Punct::LParen))?.into_tuple();
589        let (value, rest, warnings) = T::from_tokens(&rest)?.into_tuple();
590        let ((), rest, _) = parse_token(&rest, &Token::Punct(Punct::RParen))?.into_tuple();
591        Ok(Parsed {
592            value: Self(value),
593            rest,
594            diagnostics: warnings,
595        })
596    }
597}
598
599#[derive(Debug)]
600struct InSquares<T>(pub T);
601
602impl<T> FromTokens for InSquares<T>
603where
604    T: FromTokens,
605{
606    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
607    where
608        Self: Sized,
609    {
610        let ((), rest, _) = parse_token(input, &Token::Punct(Punct::LSquare))?.into_tuple();
611        let (value, rest, warnings) = T::from_tokens(&rest)?.into_tuple();
612        let ((), rest, _) = parse_token(&rest, &Token::Punct(Punct::RSquare))?.into_tuple();
613        Ok(Parsed {
614            value: Self(value),
615            rest,
616            diagnostics: warnings,
617        })
618    }
619}
620
621fn parse_token_if<F, R>(input: &TokenSlice, parse: F) -> ParseResult<R>
622where
623    F: Fn(&Token) -> Option<R>,
624{
625    if let Some(token) = input.get_token(0) {
626        if let Some(result) = parse(token) {
627            return Ok(Parsed::ok(result, input.subslice(1..input.len())));
628        }
629    }
630    Err(ParseError::Mismatch(Diagnostics::default()))
631}
632
633fn _parse_token(input: &TokenSlice, token: &Token) -> ParseResult<Token> {
634    if let Some(rest) = input.skip(token) {
635        Ok(Parsed::ok(input.first().token.clone(), rest))
636    } else {
637        Err(ParseError::Mismatch(
638            input.error(format!("expecting {token}")).into(),
639        ))
640    }
641}
642
643fn parse_token(input: &TokenSlice, token: &Token) -> ParseResult<()> {
644    if let Some(rest) = input.skip(token) {
645        Ok(Parsed::ok((), rest))
646    } else {
647        Err(ParseError::Mismatch(
648            input.error(format!("expecting {token}")).into(),
649        ))
650    }
651}
652
653fn parse_syntax(input: &TokenSlice, syntax: &str) -> ParseResult<()> {
654    if let Some(rest) = input.skip_syntax(syntax) {
655        Ok(Parsed::ok((), rest))
656    } else {
657        Err(ParseError::Mismatch(
658            input.error(format!("expecting {syntax}")).into(),
659        ))
660    }
661}
662
663pub type VarList = Punctuated<VarRange>;
664
665pub struct Number(f64);
666
667impl Debug for Number {
668    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
669        write!(f, "{:?}", self.0)
670    }
671}
672
673impl FromTokens for Number {
674    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
675    where
676        Self: Sized,
677    {
678        parse_token_if(input, |token| token.as_number().map(Number))
679            .map_err(|_| ParseError::Mismatch(input.error(String::from("expecting number")).into()))
680    }
681}
682
683#[derive(Debug)]
684pub struct Integer(i64);
685
686impl FromTokens for Integer {
687    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
688    where
689        Self: Sized,
690    {
691        parse_token_if(input, |token| token.as_integer().map(Integer)).map_err(|_| {
692            ParseError::Mismatch(input.error(String::from("expecting integer")).into())
693        })
694    }
695}
696
697pub enum VarRange {
698    Single(Identifier),
699    Range(Identifier, Identifier),
700    All,
701}
702
703impl Debug for VarRange {
704    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
705        match self {
706            Self::Single(var) => write!(f, "{var:?}"),
707            Self::Range(from, to) => write!(f, "{from:?} TO {to:?}"),
708            Self::All => write!(f, "ALL"),
709        }
710    }
711}
712
713impl FromTokens for VarRange {
714    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
715    where
716        Self: Sized,
717    {
718        if let Ok(Parsed { rest, .. }) = parse_token(input, &Token::Punct(Punct::All)) {
719            Ok(Parsed::ok(Self::All, rest))
720        } else {
721            let (from, rest, _) = parse_id(input)?.into_tuple();
722            if let Ok(Parsed { rest, .. }) = parse_token(&rest, &Token::Punct(Punct::To)) {
723                if let Ok(p) = parse_id(&rest) {
724                    return Ok(p.map(|to| Self::Range(from, to)));
725                }
726            }
727            Ok(Parsed::ok(Self::Single(from), rest))
728        }
729    }
730}
731
732fn parse_id(input: &TokenSlice) -> ParseResult<Identifier> {
733    let mut iter = input.iter();
734    if let Some(LexToken {
735        token: Token::Id(id),
736        ..
737    }) = iter.next()
738    {
739        Ok(Parsed::ok(id.clone(), iter.remainder()))
740    } else {
741        Err(ParseError::Mismatch(
742            input.error("Syntax error expecting identifier.").into(),
743        ))
744    }
745}
746
747fn parse_format(input: &TokenSlice) -> ParseResult<AbstractFormat> {
748    let mut iter = input.iter();
749    if let Some(LexToken {
750        token: Token::Id(id),
751        ..
752    }) = iter.next()
753    {
754        if let Ok(format) = id.0.as_ref().parse() {
755            return Ok(Parsed::ok(format, iter.remainder()));
756        }
757    }
758    Err(ParseError::Mismatch(
759        input.error("Syntax error expecting identifier.").into(),
760    ))
761}
762
763fn parse_string(input: &TokenSlice) -> ParseResult<String> {
764    let mut iter = input.iter();
765    if let Some(LexToken {
766        token: Token::String(s),
767        ..
768    }) = iter.next()
769    {
770        Ok(Parsed::ok(s.clone(), iter.remainder()))
771    } else {
772        Err(ParseError::Mismatch(
773            input.error("Syntax error expecting identifier.").into(),
774        ))
775    }
776}
777
778impl FromTokens for Identifier {
779    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
780    where
781        Self: Sized,
782    {
783        parse_id(input)
784    }
785}
786
787impl FromTokens for String {
788    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
789    where
790        Self: Sized,
791    {
792        parse_string(input)
793    }
794}
795
796impl FromTokens for AbstractFormat {
797    fn from_tokens(input: &TokenSlice) -> ParseResult<Self>
798    where
799        Self: Sized,
800    {
801        parse_format(input)
802    }
803}
804
805fn collect_subcommands(src: TokenSlice) -> Vec<TokenSlice> {
806    src.split(|token| token.token == Token::Punct(Punct::Slash))
807        .filter(|slice| !slice.is_empty())
808        .collect()
809}
810
811fn commands() -> &'static [Command] {
812    fn new_commands() -> Vec<Command> {
813        vec![
814            descriptives_command(),
815            crosstabs_command(),
816            ctables_command(),
817            data_list_command(),
818            Command {
819                allowed_states: EnumSet::all(),
820                enhanced_only: false,
821                testing_only: false,
822                no_abbrev: false,
823                name: "ECHO",
824                run: Box::new(|_context| todo!()),
825            },
826        ]
827    }
828
829    static COMMANDS: OnceLock<Vec<Command>> = OnceLock::new();
830    COMMANDS.get_or_init(new_commands).as_slice()
831}
832
833fn parse_command_word(lexer: &mut TokenSlice, s: &mut String, n: usize) -> bool {
834    let separator = match s.chars().next_back() {
835        Some(c) if c != '-' => " ",
836        _ => "",
837    };
838
839    match lexer.get_token(n) {
840        Some(Token::Punct(Punct::Dash)) => {
841            s.push('-');
842            true
843        }
844        Some(Token::Id(id)) => {
845            write!(s, "{separator}{id}").unwrap();
846            true
847        }
848        Some(Token::Number(number)) if number.is_sign_positive() => {
849            if let Some(integer) = number.to_exact_usize() {
850                write!(s, "{separator}{integer}").unwrap();
851                true
852            } else {
853                false
854            }
855        }
856        _ => false,
857    }
858}
859
860fn find_best_match(s: &str) -> (Option<&'static Command>, isize) {
861    let mut cm = CommandMatcher::new(s);
862    for command in commands() {
863        cm.add(command.name, command);
864    }
865    cm.get_match()
866}
867
868fn parse_command_name(
869    lexer: &mut TokenSlice,
870    error: &dyn Fn(Diagnostic),
871) -> Result<(&'static Command, usize), ()> {
872    let mut s = String::new();
873    let mut word = 0;
874    let mut missing_words = 0;
875    let mut command = None;
876    while parse_command_word(lexer, &mut s, word) {
877        (command, missing_words) = find_best_match(&s);
878        if missing_words <= 0 {
879            break;
880        }
881        word += 1;
882    }
883    if command.is_none() && missing_words > 0 {
884        s.push_str(" .");
885        (command, missing_words) = find_best_match(&s);
886        s.truncate(s.len() - 2);
887    }
888
889    match command {
890        Some(command) => Ok((command, ((word as isize + 1) + missing_words) as usize)),
891        None => {
892            if word == 0 {
893                error(
894                    lexer
895                        .subslice(0..1)
896                        .error("Syntax error expecting command name"),
897                )
898            } else {
899                error(lexer.subslice(0..word + 1).error("Unknown command `{s}`."))
900            };
901            Err(())
902        }
903    }
904}
905
906pub enum Success {
907    Success,
908    Eof,
909    Finish,
910}
911
912pub fn end_of_command(context: &Context, range: RangeFrom<usize>) -> Result<Success, ()> {
913    match context.lexer.get_token(range.start) {
914        None | Some(Token::End) => Ok(Success::Success),
915        _ => {
916            context.error(
917                context
918                    .lexer
919                    .subslice(range.start..context.lexer.len())
920                    .error("Syntax error expecting end of command."),
921            );
922            Err(())
923        }
924    }
925}
926
927fn parse_in_state(mut lexer: TokenSlice, error: &dyn Fn(Diagnostic), _state: State) {
928    match lexer.get_token(0) {
929        None | Some(Token::End) => (),
930        _ => match parse_command_name(&mut lexer, error) {
931            Ok((command, n_tokens)) => {
932                let mut context = Context {
933                    error,
934                    lexer: lexer.subslice(n_tokens..lexer.len()),
935                    command_name: Some(command.name),
936                };
937                (command.run)(&mut context);
938            }
939            Err(error) => println!("{error:?}"),
940        },
941    }
942}
943
944pub fn parse_command(lexer: TokenSlice, error: &dyn Fn(Diagnostic)) {
945    parse_in_state(lexer, error, State::Initial)
946}
947
948pub struct Context<'a> {
949    error: &'a dyn Fn(Diagnostic),
950    lexer: TokenSlice,
951    command_name: Option<&'static str>,
952}
953
954impl Context<'_> {
955    pub fn error(&self, diagnostic: Diagnostic) {
956        (self.error)(diagnostic);
957    }
958}